Ribbon 均衡策略 與 脫離 Eureka 使用、LoadBalancerClient

目錄

Ribbon 負載均衡策略概述

Ribbon 負載均衡策略配置

Ribbon 脫離 Eureka 使用

沒有 Eureka 時

已有 Eureka 時

Using the Ribbon API Directly(直接使用 Ribbon API)


Ribbon 負載均衡策略概述

1、如有微服務 mc 下有 3 個節點 A、B、C,當微服務 mk 請求微服務 mc 時,應該使用何種規則向節點 A、B、C 發起請求呢?於是 Ribbon 有了負載均衡策略。

2、Ribbon 負載均衡繼承結構如下圖所示,IRule 接口是整個規則/策略的超類/接口:

策略名 描述
BestAvailableRule 選擇一個最小的併發請求的server。逐個考察 Server,如果 Server 被 tripped(跳閘)了,則忽略,再選擇其中 ActiveRequestsCount 最小的 server。
AvailabilityFilteringRule 過濾掉那些因爲一直連接失敗的被標記爲circuit tripped的後端server,並過濾掉那些高併發的的後端server(active connections 超過配置的閾值)
WeightedResponseTimeRule 根據響應時間分配一個weight,響應時間越長,weight越小,被選中的可能性越低。
RetryRule 對選定的負載均衡策略機上重試機制。在一個配置時間段內當選擇server不成功,則一直嘗試使用subRule的方式選擇一個可用的server
RoundRobinRule 輪詢index,選擇index對應位置的server
RandomRule 隨機選擇一個server。在index上隨機,選擇index對應位置的server
ZoneAvoidanceRule 複合判斷server所在區域的性能和server的可用性選擇server

Ribbon 負載均衡策略配置

1、仍然以 <<netflix ribbon 概述與基本使用、以及 RestTemplate 概述>> 中的 3 個應用作爲本文介紹的基礎:

eurekaserverchangSha 應用作爲 Eureka 服務端, eurekaclientfood、eurekaclient_cat 作爲客戶端,eurekaclientcat 微服務請求 eurekaclientfood 微服務。

2、eureka 依賴 ribbon,導入了 eureka 客戶端組件就同時導入了 ribbon。環境:Java jdk 8 + Spring boot 2.1.3 + spring cloud Greenwich.SR1 + spring 5.1.5。

3、官網 "6.4 Customizing the Ribbon Client by Setting Properties" 有說明:

全局配置文件的選項優先級高於 @RibbonClient(configuration=MyRibbonConfig.class 代碼方式,@RibbonClient 代碼方式高於默認值。

從 1.2.0 版本開始 Spring Cloud Netflix 支持從全局文件進行配置,支持的配置如下:

<clientName>.ribbon.NFLoadBalancerClassName: Should implement ILoadBalancer
<clientName>.ribbon.NFLoadBalancerRuleClassName: Should implement IRule
<clientName>.ribbon.NFLoadBalancerPingClassName: Should implement IPing
<clientName>.ribbon.NIWSServerListClassName: Should implement ServerList
<clientName>.ribbon.NIWSServerListFilterClassName: Should implement ServerListFilter

其中的 <clientName> 爲 Eureka 註冊中心註冊好的微服務名稱,也是微服務應用配置的 spring.applicatoin.name 屬性值。

配置的屬性值爲各個接口實現類的全類名。如下所示 users 爲需要請求的微服務名稱,屬性值爲各接口實現類的全類名:

users:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule   #加權響應時間規則

4、eurekaclientcat 請求 eurekaclientfood,所以修改 eurekaclient_cat 的配置文件如下,其餘的內容都無需修改:

server:
  port: 9394

spring:
  application:
    name: eureka-client-cat  #微服務名稱

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9393/eureka/ #eureka 服務器地址
  instance:
    prefer-ip-address: true # IP 地址代替主機名註冊
    instance-id: changSha-cat # 微服務實例id名稱

#EUREKA-CLIENT-FOOD 請求的微服務名稱,即對方的 spring.application.name 屬性值
EUREKA-CLIENT-FOOD:
  ribbon:
    #隨機規則,對 EUREKA-CLIENT-FOOD 微服務下的節點隨機訪問
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

5、啓動 eurekaserverchangSha 、eurekaclient_cat  應用 ,然後使用下面的命令再啓動打包好的 eurekaclient_food 3 個實例,使用不同的端口以及實例 id:

java -jar eurekaclient_food-0.0.1-SNAPSHOT.jar --server.port=9396 --eureka.instance.instance-id=changSha-food-9396
java -jar eurekaclient_food-0.0.1-SNAPSHOT.jar --server.port=9397 --eureka.instance.instance-id=changSha-food-9397
java -jar eurekaclient_food-0.0.1-SNAPSHOT.jar --server.port=9398 --eureka.instance.instance-id=changSha-food-9398

此時 eureka 註冊中心如下所示:

6、訪問 http://localhost:9394/getCatById?id=110 通過 changSha-cat 微服務後臺請求 Eureka-client-food 微服務下的3個節點:

如果沒有在 eurekaclient_cat  中配置隨機訪問負載均衡策略,則默認情況下是使用輪詢策略的,如上所示,顯示現在是隨機訪問的,負載均衡配置生效。其它的均衡策略也是同理。

Ribbon 脫離 Eureka 使用

Ribbon 脫離 Eureka 使用分爲兩種情況,一是項目中並沒有使用 Eureka,二是項目中已經有 Eureka

沒有 Eureka 時

1、之前說過 Eureka 無論是服務端還是客戶端都依賴了 Ribbon,所以導入了 Eureka 組件後,同時已經導入了 Ribbon 組件,所以直接編碼 Ribbon 即可。現在沒有 Eureka 時,需要單獨導入 Ribbon 組件,修改 eureka-client-cat 的 pom.xml 文件如下(此時沒有 Eureka 組件):

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

2、官網介紹地址:"How to Use Ribbon Without Eureka" ,當沒有 Eureka 時,只需要修改 application.yml 如下:

stores:
  ribbon:
    listOfServers: example.com,google.com

#stores 是一個自定義的標識符,建議寫成請求的微服務名稱,即對方的 spring.application.name 屬性值。
#listOfServers 爲請求的服務域名地址,也可以直接是 Ip:Port 格式,不用帶應用名稱,而是在代碼中寫應用名

3、現在繼續修改 eureka-client-cat 配置文件如下(此時沒有 eureka 的配置了):

server:
  port: 9394

#EUREKA-CLIENT-FOOD 純粹是一個自定義的標識,會在代碼中使用類似如下的方式進行識別,根據標識找到服務器地址,然後發起請求
#restTemplate.getForObject("http://EUREKA-CLIENT-FOOD/getHunanCuisine", String.class);#如果對方寫提供了應用名稱,則也要在URL中加上
EUREKA-CLIENT-FOOD:
  ribbon:
    #隨機規則,對 EUREKA-CLIENT-FOOD 微服務下的節點隨機訪問
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    listOfServers: 192.168.3.6:9396,192.168.3.6:9397 #請求的服務地址,ip:port,多個時使用 逗號 隔開
    #注意不用帶應用名稱,而是在代碼中寫應用名

4、eureka-client-cat 其它位置都不需要修改。後臺代碼使用:

String foodMenu = restTemplate.getForObject("http://EUREKA-CLIENT-FOOD/getHunanCuisine", String.class); 

因爲 restTemplate 具有負載均衡的能力,所以會根據標識 EUREKA-CLIENT-FOOD 查找配置文件中的服務器實際地址(listOfServers),然後根據策略發起請求。後臺代碼不變

5、此時因爲沒有使用 Eureka 客戶端,所以 Eureka 註冊中心是沒有 eureka-client-cat 微服務的,但是並不影響對 EUREKA-CLIENT-FOOD 的訪問。

總結:沒有使用 Eureka 時,先導入 Ribbon 組件,然後修改配置文件添加服務器列表,最後使用具有負載均衡能力的 RestTemplate 向目標微服務發起 http 請求。

已有 Eureka 時

1、官網文檔 Disable Eureka Use in Ribbon,對於項目中已經使用了 Eureka 時,需要 Ribbon 禁用 Eureka ,配置如下:

ribbon:
  eureka:
   enabled: false

2、ribbon.eureka.enabled=false 是 Ribbon 不再使用 Eureka 發現的服務,而使用自己 stores.ribbon.listOfServers 配置的服務。

所以僅僅是 Ribbon 不再依賴 Eureka,而不是項目中禁用 Eureka。

3、修改 eureka-client-cat 的 pom.xml 文件重新添加 spring-cloud-starter-netflix-eureka-client,然後修改配置文件如下:

server:
  port: 9394

spring:
  application:
    name: eureka-client-cat  #微服務名稱

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9393/eureka/ #eureka 服務器地址
  instance:
    prefer-ip-address: true # IP 地址代替主機名註冊
    instance-id: changSha-cat # 微服務實例id名稱

#EUREKA-CLIENT-FOOD :雖然 Ribbon 脫離 Eureka 使用可以自定義標識符,但還是建議寫成對方的微服務名稱
EUREKA-CLIENT-FOOD:
  ribbon:
    #隨機規則,對 EUREKA-CLIENT-FOOD 微服務下的節點隨機訪問
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    listOfServers: 192.168.3.6:9396 #請求的服務地址,ip:port,多個時使用 逗號 隔開。不要帶應用名稱

ribbon:
  eureka:
    enabled: false #Ribbon 禁用 Eureka,禁用後 Ribbon 自己的 *.ribbon.listOfServers 服務配置纔會生效

總結:有 Eureka 與沒有 Eureka 相比多了一個 Ribbon 禁用 Eureka 的操作,其餘是一樣的。

後臺代碼:String foodMenu = restTemplate.getForObject("http://EUREKA-CLIENT-FOOD/getHunanCuisine", String.class);(如果有應用名稱,則在 url 中加上)

原理:Ribbon 沒有脫離 Eureka 時,負載均衡請求的服務名稱 "EUREKA-CLIENT-FOOD" 會自動從 Eureka 客戶端服務發現的服務列表中進行查詢解析,然後根據實際地址發起請求。當 Ribbon 脫離了 Eureka 時,顯然無法再從 Eureka 發現的服務列表中獲取,所以需要在配置文件中使用 *.ribbon.listOfServers 進行服務配置,配置服務實際的 ip 與 端口。

因爲 EUREKA-CLIENT-FOOD.ribbon.listOfServers 只配置了一個服務器地址,所以永遠都是請求它。

Using the Ribbon API Directly(直接使用 Ribbon API)

1、可以通過 LoadBalancerClient API 來獲取請求的服務實例 org.springframework.cloud.client.ServiceInstance。

2、直接 @Autowired、@Resource 從容器中獲取 org.springframework.cloud.client.loadbalancer.LoadBalancerClient 使用即可。

3、官網文檔 "Using the Ribbon API Directly" 已經寫的很詳細,這裏在依樣畫葫蘆:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;

@RestController
public class SystemController {

    //獲取容器中創建好的 RestTemplate 實例
    @Resource
    private RestTemplate restTemplate;

    //默認已經在容器中創建好了實例,直接獲取即可
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    /**
     * localhost:9394/loadBalancerClient
     *
     * @return
     */
    @GetMapping("loadBalancerClient")
    public String testLoadBalancerClient() {
        //choose(String serviceId):服務id,沒有脫離 Eureka 時,這裏通常就是對方服務名稱,即 spring.application 屬性值
        //當 Ribbon 脫離 Eureka 時,服務id 就是與自己的配置文件 xxx.ribbon.listOfServers 中的 xxx 保持一致
        ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-CLIENT-FOOD");
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String instanceId = serviceInstance.getInstanceId();
        String serviceId = serviceInstance.getServiceId();
        URI uri = serviceInstance.getUri();
        URI storesUri = URI.create(String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort()));

        System.out.println("host:" + host);
        System.out.println("port:" + port);
        System.out.println("instanceId:" + instanceId);
        System.out.println("serviceId:" + serviceId);
        System.out.println("uri:" + uri);
        System.out.println("storesUri:" + storesUri);

        return "";
    }
}

//控制檯輸出如下:
host:192.168.3.6
port:9395
instanceId:192.168.3.6:9395
serviceId:wmx
uri:http://192.168.3.6:9395
storesUri:http://192.168.3.6:9395

演示源碼 github 地址:https://github.com/wangmaoxiong/ribbon_study

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章