微服務之間的通信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