微服務之間的通訊的方式

RestTemplate的三種使用方式

SpringCloud中服務之間的兩種調用RESTful接口通訊的方式:java

  1. RestTemplate
  2. Feign

RestTemplate是一個Http客戶端,相似於HTTPClient,org但比HTTPClient更簡單。咱們經過RestTemplate來簡單演示一下服務之間的調用,咱們使用兩個服務來作演示。一個商品服務,一個訂單服務。首先建立一個商品服務工程:
微服務之間的通訊的方式
微服務之間的通訊的方式web

選擇相應的依賴:
微服務之間的通訊的方式算法

項目建立完成後,編輯配置文件,須要配置服務的名稱以及服務註冊中心的地址:spring

spring:
  application:
    name: product

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

注:若是對eureka還不太清楚的話,能夠參考個人另外一篇關於eureka的文章:Spring Cloud Eureka-服務註冊與發現bash

不要忘了在啓動類中,加上@EnableEurekaClient註解:架構

package org.zero.example.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
}

接着建立一個controller類,用於模擬商品列表接口,提供給訂單服務調用。代碼以下:app

package org.zero.example.product.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: product
 * @description: product demo
 * @author: 01
 * @create: 2018-09-06 22:09
 **/
@RestController
@RequestMapping("/product")
public class ProductController {

    /**
     * 模擬商品列表接口
     *
     * @return product list
     */
    @GetMapping("/list")
    public List<String> list() {
        List<String> productList = new ArrayList<>();
        productList.add("肥皂");
        productList.add("可樂");

        return productList;
    }
}

而後啓動項目,啓動完成後,此時,在eureka的信息面板上應該能夠看到product註冊上去了,以下:
微服務之間的通訊的方式負載均衡


商品服務準備好後,使用一樣的步驟建立order項目,這裏就再也不贅述了。配置文件中除了服務名稱需爲order,其餘的配置項和product同樣。由於8080已經被product服務佔用了,因此還須要手動設置一下項目的端口號:
微服務之間的通訊的方式dom

新建一個ClientController類,咱們來看看RestTemplate的第一種使用方式,代碼以下:maven

package org.zero.example.order.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * @program: order
 * @description: order demo
 * @author: 01
 * @create: 2018-09-06 22:24
 **/
@RestController
@RequestMapping("/order")
public class OrderController {

    @GetMapping("/info")
    public List info() {
        // 1.第一種方式,直接使用。缺點:須要指定url地址,不靈活,也沒法適應多個地址
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject("http://localhost:8080/product/list", List.class);
    }
}

寫完後啓動項目,能夠看到order服務也註冊到eureka上了:
微服務之間的通訊的方式

接口測試結果以下,能夠看到成功調用了商品服務的接口:
微服務之間的通訊的方式


而後是RestTemplate的第二種使用方式,代碼以下:

...

import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/info")
    public List info() {
        // 2.第二種方式,藉助LoadBalancerClient獲取服務實例,缺點:須要拼接url依舊不靈活
        RestTemplate restTemplate = new RestTemplate();
        // 參數傳的是服務註冊的id
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
        String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/product/list");

        return restTemplate.getForObject(url, List.class);
    }
}

接着是RestTemplate的第三種使用方式,這種方式下咱們須要先建立一個配置類,代碼以下:

package org.zero.example.order.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @program: order
 * @description: RestTemplate Config
 * @author: 01
 * @create: 2018-09-06 22:47
 **/
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

而後在controller中注入使用,這種方式雖然最簡潔,其實本質上仍是第二種方式:

...
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/info")
    public List info() {
        // 3.第三種方式,利用@LoadBalanced註解,可在restTemplate裏使用應用名稱進行調用
        return restTemplate.getForObject("http://PRODUCT/product/list", List.class);
    }
}

負載均衡器:Ribbon

eureka是客戶端發現機制的,因此使用的是客戶端負載均衡器,所謂客戶端負載均衡,也就是說負載的策略在客戶端完成,俗稱軟負載。若是咱們的商品服務部署在多個節點上的話,當使用Feign進行服務調用的時候,默認會使用Ribbon來作負載均衡。固然使用RestTemplate的時候也是能夠結合Ribbon作負載均衡的,例如上一小節中演示的第2、三種使用RestTemplate的方式就是結合了Ribbon。

Ribbon是Netflix發佈的負載均衡器,是一種客戶端負載均衡器,運行在客戶端上,它有助於控制HTTP和TCP的客戶端的行爲。爲Ribbon配置服務提供者地址後,Ribbon就可基於某種負載均衡算法,自動地幫助服務消費者去請求。Ribbon默認爲咱們提供了不少負載均衡算法,例如輪詢、隨機等。固然,咱們也可爲Ribbon實現自定義的負載均衡算法。

咱們在配置文件中能夠自定義負載均衡策略,以下:

PRODUCT:  # 服務的名稱
  ribbon:  # 負載均衡器
    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文件配置的依賴以下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

注:若出現使用阿里雲的倉庫地址不能下載到該依賴的話,能夠嘗試使用maven中央倉庫的地址進行下載

首先到商品服務工程中,新建一個client包。原本應該是新建一個Client模塊的,可是爲了方便演示,我就直接用包了。在client包下新建一個 ProductClinet 接口,編寫代碼以下:

package org.zero.example.product.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@Component
// 此註解用於聲明一個Feign客戶端,name屬性指定服務的名稱
@FeignClient(name = "PRODUCT")
public interface ProductClinet {

    /**
     * 商品列表接口,注意這裏的uri要寫全
     *
     * @return Product List
     */
    @GetMapping("/product/list")
    List<String> list();
}

咱們在使用RestTemplate的時候,都是在訂單服務上編寫接口調用相關代碼的,可是爲何使用Feign就在商品服務上去寫這個代碼呢?這是由於使用Feign的時候,只須要經過註解就能在接口上聲明客戶端,當咱們在訂單服務裏面使用的時候,注入這個ProductClinet接口調用相應的方法便可實現商品服務接口的調用。而這些接口屬於商品服務對外暴露的接口,因爲職責的關係,因此都應該由商品服務去維護,不該該寫在訂單服務裏。

編寫好ProductClinet接口的代碼後,使用以下命令將這個項目安裝到本地的maven倉庫中:

mvn clean -Dmaven.test.skip=true install

接着到訂單服務的工程中,加入商品服務的依賴,以下:

<dependency>
    <groupId>org.zero.example</groupId>
    <artifactId>product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

而後在啓動類中,加上@EnableFeignClients註解表示開啓 Feign 客戶端以及配置client包的路徑,以下:

package org.zero.example.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients(basePackages = "org.zero.example.product.client")
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

修改 OrderController 代碼以下:

...

import org.zero.example.product.client.ProductClinet;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private ProductClinet productClinet;

    @GetMapping("/info")
    public List info() {
        return productClinet.list();
    }
}

重啓這兩個項目,測試接口以下:
微服務之間的通訊的方式

相關文章
相關標籤/搜索