Spring Cloud Eureka 服務註冊中心3:Ribbon服務消費者

語言: CN / TW / HK

在微服務架構中,業務都會被拆分成一個獨立的服務,服務與服務的通訊是基於 Http Restful 的。Spring Cloud 有兩種服務調用方式,一種是Ribbon + RestTemplate消費服務,另一種是 Feign消費服務。這篇我們主要講解 Ribbon 消費服務。

官網地址:https://docs.spring.io/spring-cloud-netflix/docs/2.2.8.RELEASE/reference/html/#spring-cloud-ribbon

Ribbon 是一個基於 HTTP 和 TCP 的客户端負載均衡器,它可以在通過客户端中配置的 RibbonServerList 服務端列表去輪詢訪問以達到均衡負載的作用。當 Ribbon 與 Eureka 聯合使用時,Ribbon 的服務實例清單 RibbonServerList 會被 DiscoveryEnabledNIWSServerList 重寫,擴展成從 Eureka 註冊中心中獲取服務端列表。同時它也會用 NIWSDiscoveryPing 來取代 IPing,它將職責委託給 Eureka 來確定服務端是否已經啟動。

高版本中沒有找到Ribbon的文檔。

 

一、準備工作

1.1、創建啟動註冊中心-服務端

 請參考:Spring Cloud Eureka 服務註冊中心組建1:搭建註冊中心

1.2、創建啟動註冊中心-客户端

請參考:Spring Cloud Eureka 服務註冊中心組建1:搭建註冊中心

在啟動之前,我們需要針對上篇文章的代碼進行改造,添加一個訪問接口,輸出當前客户端的端口。

代碼如下:

@RestController
public class ClientController {

    @Value("${server.port}")
    private String port;

    @GetMapping("/client")
    public void client() {
        System.out.println("my port is " + port);
    }

}

我們創建啟動2個客户端,一個為piao-client,端口為2010。一個為piao-client2,端口為2011。

如果不想創建多個項目,可以請考這篇文章:IDEA 啟動多個SpringBoot項目不同端口

啟動成功如下:

 

二、創建服務消費者

2.1、創建 piao-ribbon 項目。

我們這裏使用IDEA創建,文件-》新建-》項目-》下一步到如下的頁面:

然後在點下一步,直到完成。

 

2.2、修改依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.piao</groupId>
    <artifactId>piao-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>piao-ribbon</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

2.3、添加配置

編輯項目的 application.properites 文件,添加如下相關配置:

#服務註冊中心端口號
server.port=2005

#指定服務名稱
spring.application.name=piao-ribbon
#指定服務註冊中心的地址
eureka.client.serviceUrl.defaultZone=http://localhost:2000/eureka/

在啟動類上添加 @EnableDiscoveryClient 註解。使用@LoadBalanced註解賦予RestTemplate負載均衡的能力。

@EnableDiscoveryClient
@SpringBootApplication
public class PiaoRibbonApplication {

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

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

}

Spring Cloud Ribbon 中實現客户端負載均衡的原理簡單來説:就是通過 LoadBalancerInterceptor 攔截器對 RestTemplate 的請求進行攔截,並利用 Spring Cloud 的負載均衡器 LoadBalancerClient 將一服務名為 host 的 URI 轉換成具體的服務實例地址的過程。

 

2.4、編寫一個消費者的請求類

在該接口中,通過前面定義的 RestTemplate 來實現對 PIAO-CLIENT  服務提供的 /client 接口進行負載均衡的調用。

@RestController
public class ConsumerController {

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/consumer")
    public String helloConsumer() {
        // 通過服務名調用服務提供者的接口
        return restTemplate.getForEntity("http://PIAO-CLIENT/client", String.class).getBody();
    }

}

 

2.5、啟動服務並訪問

我們訪問註冊中心地址:http://127.0.0.1:2000/

訪問地址:http://127.0.0.1:2005/consumer

第一次訪問:

第二次訪問:

我們看到了請求分別調用了我們之前準備的 PIAO-CLIENT 接口。實現負載均衡的效果。

Ribbon 輸出了當前客户端維護的 IAO-CLIENT 的服務列表情況。其中包含了各個實例的位置,Ribbon 就是按照此信息進行輪訓訪問,以實現基於客户端的負載均衡。除此之外還輸出了一些其他非常有用的信息,如對各個實例的請求總數量、第一次連接信息、上一次連接信息、總的請求失敗數量等。

 

三、Ribbon 重試機制

Spring Cloud Eureka 實現的服務治理機制強調了 CAP 原理中的 AP,即可用性與可靠性,犧牲了一定的一致性(在極端情況下它寧願接受故障實例也不要丟掉"健康"實例)。但不論是由於觸發了保護機制還是服務剔除的延遲,引起服務調用到故障實例的時候,我們還是希望能夠增強對這類問題的容錯。所以,我們在實現服務調用的時候通常會加入一些重試機制。

從 Camden SR2 版本開始,Spring Cloud 就整合了 Spring Retry 來增強 RestTemplate 的重試能力,對於開發者來説只需通過簡單的配置,原來那些通過 RestTemplate 實現的服務訪問就會自動根據配置來實現重試策略。

3.1、添加依賴

<!-- 重試機制-->
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

 

3.2、修改配置

編輯項目的 application.properites 文件,對 ribbon 重試相關參數進行設置。

spring.cloud.loadbalancer.retry.enabled 參數用來開啟重試機制,因為默認是 true(開啟)。因此只要引入 spring-retry 依賴就可以自動實現重試功能。如果要將其關閉,只要將其設為 false 即可。

全局配置:

#斷路器的超時時長需要大於Ribbon的超時時間,不然不會觸發重試
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

#請求連接超時時間(毫秒)
ribbon.ConnectTimeout=1000
#請求處理的超時時間(毫秒)
ribbon.ReadTimeout=3000
#對所有請求都進行重試(是否所有操作都重試,若false則僅get請求重試)
ribbon.OkToRetryOnAllOperations=true
#切換實例的重試次數
ribbon.MaxAutoRetriesNextServer=2
#對當前實例的重試次數
ribbon.MaxAutoRetries=1
#修改負載均衡算法
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

指定服務配置:

#斷路器的超時時長需要大於Ribbon的超時時間,不然不會觸發重試
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

#請求連接超時時間(毫秒)
piao-client.ribbon.ConnectTimeout=1000
#請求處理的超時時間(毫秒)
piao-client.ribbon.ReadTimeout=3000
#對所有請求都進行重試(是否所有操作都重試,若false則僅get請求重試)
piao-client.ribbon.OkToRetryOnAllOperations=true
#切換實例的重試次數
piao-client.ribbon.MaxAutoRetriesNextServer=2
#對當前實例的重試次數
piao-client.ribbon.MaxAutoRetries=1
#修改負載均衡算法
piao-client.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

配置説明:當訪問到故障請求的時候,它會再嘗試訪問一次當前實例(次數由 MaxAutoRetries 配置),如果不行,就換一個實例進行訪問,如果還不行,再換一次實例訪問(更換次數由 MaxAutoRetriesNextServer 配置),如果依然不行,返回失敗信息。

Ribbon的負載均衡策略:

  • com.netflix.loadbalancer.RandomRule:從提供服務的實例中以隨機的方式;
  • com.netflix.loadbalancer.RoundRobinRule:以線性輪詢的方式,就是維護一個計數器,從提供服務的實例中按順序選取,第一次選第一個,第二次選第二個,以此類推,到最後一個以後再從頭來過;
  • com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基礎上添加重試機制,即在指定的重試時間內,反覆使用線性輪詢策略來選擇可用實例;
  • com.netflix.loadbalancer.WeightedResponseTimeRule:對RoundRobinRule的擴展,響應速度越快的實例選擇權重越大,越容易被選擇;
  • com.netflix.loadbalancer.BestAvailableRule:選擇併發較小的實例;
  • com.netflix.loadbalancer.AvailabilityFilteringRule:先過濾掉故障實例,再選擇併發較小的實例;
  • com.netflix.loadbalancer.ZoneAwareLoadBalancer:採用雙重過濾,同時過濾不是同一區域的實例和故障實例,選擇併發較小的實例。