微服务之间的通信RestTemplate


RestTemplate的三种使用方式

SpringCloud中服务之间的两种调用RESTful接口通信的方式:

  1. RestTemplate
  2. Feign

RestTemplate是一个Http客户端,类似于HTTPClient,org但比HTTPClient更简单。我们通过RestTemplate来简单演示一下服务之间的调用,我们使用两个服务来做演示。一个商品服务,一个订单服务。首先创建一个商品服务工程:
微服务之间的通信的方式
微服务之间的通信的方式

选择相应的依赖:
微服务之间的通信的方式

项目创建完成后,编辑配置文件,需要配置服务的名称以及服务注册中心的地址:

  1.  
    spring:
  2.  
    application:
  3.  
    name: product
  4.  
     
  5.  
    eureka:
  6.  
    client:
  7.  
    service-url:
  8.  
    defaultZone: http://localhost:8761/eureka/
  9.  
    instance:
  10.  
    prefer-ip-address: true

注:如果对eureka还不太清楚的话,可以参考我的另一篇关于eureka的文章:Spring Cloud Eureka-服务注册与发现

不要忘了在启动类中,加上@EnableEurekaClient注解:

  1.  
    package org.zero.example.product;
  2.  
     
  3.  
    import org.springframework.boot.SpringApplication;
  4.  
    import org.springframework.boot.autoconfigure.SpringBootApplication;
  5.  
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  6.  
     
  7.  
    @SpringBootApplication
  8.  
    @EnableEurekaClient
  9.  
    public class ProductApplication {
  10.  
     
  11.  
    public static void main(String[] args) {
  12.  
    SpringApplication.run(ProductApplication.class, args);
  13.  
    }
  14.  
    }

接着创建一个controller类,用于模拟商品列表接口,提供给订单服务调用。代码如下:

  1.  
    package org.zero.example.product.controller;
  2.  
     
  3.  
    import org.springframework.web.bind.annotation.GetMapping;
  4.  
    import org.springframework.web.bind.annotation.RequestMapping;
  5.  
    import org.springframework.web.bind.annotation.RestController;
  6.  
     
  7.  
    import java.util.ArrayList;
  8.  
    import java.util.List;
  9.  
     
  10.  
    /**
  11.  
    * @program: product
  12.  
    * @description: product demo
  13.  
    * @author: 01
  14.  
    * @create: 2018-09-06 22:09
  15.  
    **/
  16.  
    @RestController
  17.  
    @RequestMapping("/product")
  18.  
    public class ProductController {
  19.  
     
  20.  
    /**
  21.  
    * 模拟商品列表接口
  22.  
    *
  23.  
    * @return product list
  24.  
    */
  25.  
    @GetMapping("/list")
  26.  
    public List<String> list() {
  27.  
    List<String> productList = new ArrayList<>();
  28.  
    productList.add( "肥皂");
  29.  
    productList.add( "可乐");
  30.  
     
  31.  
    return productList;
  32.  
    }
  33.  
    }

然后启动项目,启动完成后,此时,在eureka的信息面板上应该可以看到product注册上去了,如下:
微服务之间的通信的方式


商品服务准备好后,使用同样的步骤创建order项目,这里就不再赘述了。配置文件中除了服务名称需为order,其他的配置项和product一样。因为8080已经被product服务占用了,所以还需要手动设置一下项目的端口号:
微服务之间的通信的方式

新建一个ClientController类,我们来看看RestTemplate的第一种使用方式,代码如下:

  1.  
    package org.zero.example.order.controller;
  2.  
     
  3.  
    import org.springframework.web.bind.annotation.GetMapping;
  4.  
    import org.springframework.web.bind.annotation.RequestMapping;
  5.  
    import org.springframework.web.bind.annotation.RestController;
  6.  
    import org.springframework.web.client.RestTemplate;
  7.  
     
  8.  
    import java.util.List;
  9.  
     
  10.  
    /**
  11.  
    * @program: order
  12.  
    * @description: order demo
  13.  
    * @author: 01
  14.  
    * @create: 2018-09-06 22:24
  15.  
    **/
  16.  
    @RestController
  17.  
    @RequestMapping("/order")
  18.  
    public class OrderController {
  19.  
     
  20.  
    @GetMapping("/info")
  21.  
    public List info() {
  22.  
    // 1.第一种方式,直接使用。缺点:需要指定url地址,不灵活,也无法适应多个地址
  23.  
    RestTemplate restTemplate = new RestTemplate();
  24.  
    return restTemplate.getForObject("http://localhost:8080/product/list", List.class);
  25.  
    }
  26.  
    }

写完后启动项目,可以看到order服务也注册到eureka上了:
微服务之间的通信的方式

接口测试结果如下,可以看到成功调用了商品服务的接口:
微服务之间的通信的方式


然后是RestTemplate的第二种使用方式,代码如下:

  1.  
    ...
  2.  
     
  3.  
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
  4.  
     
  5.  
    @RestController
  6.  
    @RequestMapping("/order")
  7.  
    public class OrderController {
  8.  
     
  9.  
    @Autowired
  10.  
    private LoadBalancerClient loadBalancerClient;
  11.  
     
  12.  
    @GetMapping("/info")
  13.  
    public List info() {
  14.  
    // 2.第二种方式,借助LoadBalancerClient获取服务实例,缺点:需要拼接url依旧不灵活
  15.  
    RestTemplate restTemplate = new RestTemplate();
  16.  
    // 参数传的是服务注册的id
  17.  
    ServiceInstance serviceInstance = loadBalancerClient.choose( "PRODUCT");
  18.  
    String url = String.format( "http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/product/list");
  19.  
     
  20.  
    return restTemplate.getForObject(url, List.class);
  21.  
    }
  22.  
    }

接着是RestTemplate的第三种使用方式,这种方式下我们需要先创建一个配置类,代码如下:

  1.  
    package org.zero.example.order.config;
  2.  
     
  3.  
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
  4.  
    import org.springframework.context.annotation.Bean;
  5.  
    import org.springframework.context.annotation.Configuration;
  6.  
    import org.springframework.web.client.RestTemplate;
  7.  
     
  8.  
    /**
  9.  
    * @program: order
  10.  
    * @description: RestTemplate Config
  11.  
    * @author: 01
  12.  
    * @create: 2018-09-06 22:47
  13.  
    **/
  14.  
    @Configuration
  15.  
    public class RestTemplateConfig {
  16.  
     
  17.  
    @Bean
  18.  
    @LoadBalanced
  19.  
    public RestTemplate restTemplate(){
  20.  
    return new RestTemplate();
  21.  
    }
  22.  
    }

然后在controller中注入使用,这种方式虽然最简洁,其实本质上还是第二种方式:

  1.  
    ...
  2.  
    @RestController
  3.  
    @RequestMapping("/order")
  4.  
    public class OrderController {
  5.  
     
  6.  
    @Autowired
  7.  
    private RestTemplate restTemplate;
  8.  
     
  9.  
    @GetMapping("/info")
  10.  
    public List info() {
  11.  
    // 3.第三种方式,利用@LoadBalanced注解,可在restTemplate里使用应用名称进行调用
  12.  
    return restTemplate.getForObject("http://PRODUCT/product/list", List.class);
  13.  
    }
  14.  
    }

负载均衡器:Ribbon

eureka是客户端发现机制的,所以使用的是客户端负载均衡器,所谓客户端负载均衡,也就是说负载的策略在客户端完成,俗称软负载。如果我们的商品服务部署在多个节点上的话,当使用Feign进行服务调用的时候,默认会使用Ribbon来做负载均衡。当然使用RestTemplate的时候也是可以结合Ribbon做负载均衡的,例如上一小节中演示的第二、三种使用RestTemplate的方式就是结合了Ribbon。

Ribbon是Netflix发布的负载均衡器,是一种客户端负载均衡器,运行在客户端上,它有助于控制HTTP和TCP的客户端的行为。为Ribbon配置服务提供者地址后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。

我们在配置文件中可以自定义负载均衡策略,如下:

  1.  
    PRODUCT: # 服务的名称
  2.  
    ribbon: # 负载均衡器
  3.  
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 规则完整的类名,这里使用的是随机

注:如非必须,一般使用默认的轮询策略即可

Ribbon特性:

  • 服务发现
  • 服务选择规则
  • 服务监听
  • ServerList,获取可用服务列表
  • IRule,选择最终调用,即负载策略
  • ServerListFilter,过滤不可用地址

在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。下图展示了Ribbon与Eureka配合使用时的架构:
微服务之间的通信的方式


Feign的使用

Feign是从Netflix中分离出来的轻量级项目,是一个声明式的REST客户端,它的出现使得我们在服务中编写REST客户端变得更加容易。利用 Feign 可以创建一个接口并对它进行注解,该接口就会具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与×××。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket,Feign 最初是为了降低统一绑定 Denominator 到 HTTP API 的复杂度,不区分是否支持 Restful。

Spring Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign。

Feign特性:

  • 声明式REST客户端(伪RPC)
  • 采用了基于接口的注解
  • 同样使用ribbon做负载均衡器

接下来我们尝试一下使用Feign编写REST客户端,实现订单服务调用商品服务接口,看看Feign到底有多方便。在商品和订单服务的项目中,都加入Feign的依赖,pom.xml文件配置的依赖如下:

  1.  
    <dependency>
  2.  
    <groupId>org.springframework.cloud</groupId>
  3.  
    <artifactId>spring-cloud-starter-openfeign</artifactId>
  4.  
    </dependency>

注:若出现使用阿里云的仓库地址不能下载到该依赖的话,可以尝试使用maven中央仓库的地址进行下载

首先到商品服务工程中,新建一个client包。本来应该是新建一个Client模块的,但是为了方便演示,我就直接用包了。在client包下新建一个 ProductClinet 接口,编写代码如下:

  1.  
    package org.zero.example.product.client;
  2.  
     
  3.  
    import org.springframework.cloud.openfeign.FeignClient;
  4.  
    import org.springframework.web.bind.annotation.GetMapping;
  5.  
     
  6.  
    import java.util.List;
  7.  
     
  8.  
    @Component
  9.  
    // 此注解用于声明一个Feign客户端,name属性指定服务的名称
  10.  
    @FeignClient(name = "PRODUCT")
  11.  
    public interface ProductClinet {
  12.  
     
  13.  
    /**
  14.  
    * 商品列表接口,注意这里的uri要写全
  15.  
    *
  16.  
    * @return Product List
  17.  
    */
  18.  
    @GetMapping("/product/list")
  19.  
    List<String> list();
  20.  
    }

我们在使用RestTemplate的时候,都是在订单服务上编写接口调用相关代码的,但是为什么使用Feign就在商品服务上去写这个代码呢?这是因为使用Feign的时候,只需要通过注解就能在接口上声明客户端,当我们在订单服务里面使用的时候,注入这个ProductClinet接口调用相应的方法即可实现商品服务接口的调用。而这些接口属于商品服务对外暴露的接口,由于职责的关系,所以都应该由商品服务去维护,不应该写在订单服务里。

编写好ProductClinet接口的代码后,使用如下命令将这个项目安装到本地的maven仓库中:

mvn clean -Dmaven.test.skip=true install

接着到订单服务的工程中,加入商品服务的依赖,如下:

  1.  
    <dependency>
  2.  
    <groupId>org.zero.example</groupId>
  3.  
    <artifactId>product</artifactId>
  4.  
    <version>0.0.1-SNAPSHOT</version>
  5.  
    </dependency>

然后在启动类中,加上@EnableFeignClients注解表示开启 Feign 客户端以及配置client包的路径,如下:

  1.  
    package org.zero.example.order;
  2.  
     
  3.  
    import org.springframework.boot.SpringApplication;
  4.  
    import org.springframework.boot.autoconfigure.SpringBootApplication;
  5.  
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  6.  
    import org.springframework.cloud.openfeign.EnableFeignClients;
  7.  
     
  8.  
    @EnableEurekaClient
  9.  
    @SpringBootApplication
  10.  
    @EnableFeignClients(basePackages = "org.zero.example.product.client")
  11.  
    public class OrderApplication {
  12.  
     
  13.  
    public static void main(String[] args) {
  14.  
    SpringApplication.run(OrderApplication.class, args);
  15.  
    }
  16.  
    }

修改 OrderController 代码如下:

  1.  
    ...
  2.  
     
  3.  
    import org.zero.example.product.client.ProductClinet;
  4.  
     
  5.  
    @RestController
  6.  
    @RequestMapping("/order")
  7.  
    public class OrderController {
  8.  
     
  9.  
    @Autowired
  10.  
    private ProductClinet productClinet;
  11.  
     
  12.  
    @GetMapping("/info")
  13.  
    public List info() {
  14.  
    return productClinet.list();
  15.  
    }
  16.  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM