Spring Cloud微服務開發筆記5——Ribbon負載均衡策略規則定制


上一篇文章單獨介紹了Ribbon框架的使用,及其如何實現客戶端對服務訪問的負載均衡,但只是單獨從Ribbon框架實現,沒有涉及spring cloud。本文着力介紹Ribbon的負載均衡機制,下一篇文章再在spring中繼承Ribbon。

Ribbon負載均衡器

上一篇文章我們已經實現了一個客戶端負載均衡請求web服務的示例。

當時,我們留了一個伏筆,其中的負載均衡的規則策略可以定制,那么本文着重研究策略定制這部分內容,其他的ribbon客戶端的構建和請求方法請參見上一篇文章。

ribbon的負載均衡的策略規則是獨立的,即這部分功能可以獨立於時間的客戶端構造和請求發送。我們需要做的是,實現一個IRule接口的對象,對象里面當然會有一些需要你去覆蓋實現的方法,這些方法中需要用代碼實現你定制的服務器選擇策略,但不包括實際的網絡請求操作。

除了可以自己定制,Ribbon已經為我們設計了幾個現成的規則策略,分別對應於多個不同IRule實現類。我們只需要將這些類對象傳給負載均衡器ILoadBalancer的chooseServer()就可以了。其中,默認情況下,BaseLoadBalancer會選擇輪詢各個server的策略方式,叫做RoundRobinRule。源碼如下:

public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnections.PrimeConnectionListener, IClientConfigAware { private static Logger logger = LoggerFactory .getLogger(BaseLoadBalancer.class); private final static IRule DEFAULT_RULE = new RoundRobinRule(); private final static SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy(); private static final String DEFAULT_NAME = "default"; private static final String PREFIX = "LoadBalancer_"; protected IRule rule = DEFAULT_RULE; protected IPingStrategy pingStrategy = DEFAULT_PING_STRATEGY; protected IPing ping = null; @Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL) protected volatile List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>()); @Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL) protected volatile List<Server> upServerList = Collections .synchronizedList(new ArrayList<Server>());

好,先說到這里,我們來實驗一下默認的規則策略:

第一步,我們先構建一個負載均衡器,類型BaseLoadBalancer,它是ILoadBalancer的實現類。

負載均衡器,需要配置兩樣東西:

1、服務地址列表——誰來參選?

2、選擇策略規則——怎么選?

第二步,按照服務方的地址端口列表,配置一個Server的List。添加給負載均衡器。

第三步,構造或選擇一個IRule實現類,通過ConfigurationMannager來配置【客戶端名稱】.ribbon.NFLoadBalancerRuleClassName屬性,將配置鍵賦予一個規則類。這里我們不操作,使用默認的。

package com.happybks.invokers; import java.util.ArrayList; import java.util.List; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; public class BalancerApplication { public static void main(String[] args) { ILoadBalancer balancer=new BaseLoadBalancer(); List<Server> servers = new ArrayList<Server>(); servers.add(new Server("127.0.0.1",8091)); servers.add(new Server("127.0.0.1",8092)); balancer.addServers(servers); for(int i=0;i<10;i++) { Server choosedServer = balancer.chooseServer(null); System.out.println(choosedServer); } } } 

默認策略,就用BaseLoadBalancer類默認初始化定義好的,chooseServer方法會為我們選擇已有的RoundRobinRule。

(本文出自ochina博主happBKs的博文:https://my.oschina.net/happyBKs/blog/1787825)

源碼如下:

private final static IRule DEFAULT_RULE = new RoundRobinRule();
    /* * Get the alive server dedicated to key * * @return the dedicated server */ public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter.increment(); if (rule == null) { return null; } else { try { return rule.choose(key); } catch (Exception e) { logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e); return null; } } }

我們運行代碼:10次選擇server的結果打印如下:

16:41:10.754 [main] WARN com.netflix.config.sources.URLConfigurationSource - No URLs will be polled as dynamic configuration sources. 16:41:10.758 [main] INFO com.netflix.config.sources.URLConfigurationSource - To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath. 16:41:10.766 [main] INFO com.netflix.config.DynamicPropertyFactory - DynamicPropertyFactory is initialized with configuration sources: com.netflix.config.ConcurrentCompositeConfiguration@75bd9247 16:41:10.818 [main] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [default]: clearing server list (SET op) 16:41:10.819 [main] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [default]: addServer [127.0.0.1:8091] 16:41:10.819 [main] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [default]: addServer [127.0.0.1:8092] 127.0.0.1:8092 127.0.0.1:8091 127.0.0.1:8092 127.0.0.1:8091 127.0.0.1:8092 127.0.0.1:8091 127.0.0.1:8092 127.0.0.1:8091 127.0.0.1:8092 127.0.0.1:8091 

 

關於負載均衡相關的四個配置項

這些配置項的前綴是【客戶端名稱】.ribbon

The supported properties are listed below and should be prefixed by <clientName>.ribbon.:

  • NFLoadBalancerClassName: should implement ILoadBalancer
  • NFLoadBalancerRuleClassName: should implement IRule
  • NFLoadBalancerPingClassName: should implement IPing
  • NIWSServerListClassName: should implement ServerList
  • NIWSServerListFilterClassName should implement ServerListFilter

 

其中比較重要的是NFLoadBalancerRuleClassName,我們可以通過這個配置項定制需要的負載均衡規則,可以是ribbon提供的原生的幾種規則類,也可以是自己實現的規則類,這些類都實現了IRule接口。

NFLoadBalancerPingClassName用於配置查看服務器是否存活。

NFLoadBalancerRuleClassName指定負載均衡器的實現類。當然,可以設置自己實現的負載均衡器。

NIWSServerListClassName是服務器列表的處理類,用來維護服務器列表的。Ribbon已經實現了動態服務器列表。

NIWSServerListFilterClassName是服務器的攔截類。

 

負載均衡的兩種配置方法:

一種直接調用ConfigurationManager獲取配置實例,然后設置配置屬性;一種是在application.yml中配置。

 

自定義策略規則:

下面我們以一個示例來構建一個自己的負載均衡規則。

示例:構建一個60%的概率選擇8091,40%概率選擇8092的規則

我們構建一個實現IRule接口的實現類:

package com.happybks.invokers; import java.util.List; import java.util.Random; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.Server; public class MyProbabilityRandomRule implements IRule { ILoadBalancer balancer = new BaseLoadBalancer(); @Override public Server choose(Object key) { List<Server> allServers = balancer.getAllServers(); Random random = new Random(); final int number = random.nextInt(10); if (number < 7) { return findServer(allServers,8091); } return findServer(allServers,8092); } private Server findServer(List<Server> allServers, int port) { for (Server server : allServers) { if (server.getPort() == port) { return server; } } System.out.println("NULL port="+port); return null; } @Override public void setLoadBalancer(ILoadBalancer lb) { this.balancer = lb; } @Override public ILoadBalancer getLoadBalancer() { return this.balancer; } } 

客戶端的請求程序這里我們還是直接在一個普通的main方法中實現

我們首先需要配置請求服務器列表,這個上篇文章已經介紹過。

之后我們對對應的客戶端配置它的ribbon.NFLoadBalancerRuleClassName配置為我們剛才定義的那個實現了IRule的實現類的類名全名,注意:是IRule實現類的全名,一個字符串,不是class。

package com.happybks.invokers; import com.netflix.client.ClientException; import com.netflix.client.ClientFactory; import com.netflix.client.http.HttpRequest; import com.netflix.client.http.HttpResponse; import com.netflix.config.ConfigurationManager; import com.netflix.niws.client.http.RestClient; public class MyRuleClientApplication { public static void main(String[] args) throws Exception { // 1、設置請求的服務器 ConfigurationManager.getConfigInstance().setProperty("happybks-client.ribbon.listOfServers", "localhost:8091,localhost:8092"); // 1 // 2、 配置規則處理類 //本示例略,先默認使用其默認負載均衡策略規則 ConfigurationManager.getConfigInstance().setProperty("happybks-client.ribbon.NFLoadBalancerRuleClassName",MyProbabilityRandomRule.class.getName()); // 3、獲取 REST 請求客戶端 RestClient client = (RestClient) ClientFactory.getNamedClient("happybks-client"); // 4、創建請求實例 HttpRequest request = HttpRequest.newBuilder().uri("/carsInfo/onsale").build(); // 5、發 送 10 次請求到服務器中 for (int i = 0; i < 10; i++) { System.out.println("the "+(i+1)+"th: "); HttpResponse response = client.executeWithLoadBalancer(request); String result = response.getEntity(String.class); System.out.println(result); } } } 

響應請求的服務方程序請參見之前的文章,我們不再累述。然后我們看看運行結果:

22:52:27.695 [main] WARN com.netflix.config.sources.URLConfigurationSource - No URLs will be polled as dynamic configuration sources. 22:52:27.701 [main] INFO com.netflix.config.sources.URLConfigurationSource - To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath. 22:52:27.834 [main] INFO com.netflix.config.DynamicPropertyFactory - DynamicPropertyFactory is initialized with configuration sources: com.netflix.config.ConcurrentCompositeConfiguration@97e1986 22:52:28.787 [main] INFO com.netflix.http4.ConnectionPoolCleaner - Initializing ConnectionPoolCleaner for NFHttpClient:happybks-client 22:52:28.798 [Connection pool clean up thread] DEBUG com.netflix.http4.ConnectionPoolCleaner - Connection pool clean up started for client happybks-client 22:52:28.799 [Connection pool clean up thread] DEBUG com.netflix.http4.MonitoredConnectionManager - Closing expired connections 22:52:28.799 [Connection pool clean up thread] DEBUG com.netflix.http4.NamedConnectionPool - Closing expired connections 22:52:28.799 [Connection pool clean up thread] DEBUG com.netflix.http4.MonitoredConnectionManager - Closing connections idle longer than 30000 MILLISECONDS 22:52:28.800 [Connection pool clean up thread] DEBUG com.netflix.http4.NamedConnectionPool - Closing connections idle longer than 30000 MILLISECONDS 22:52:29.032 [main] WARN com.netflix.client.ClientFactory - Class com.happybks.invokers.MyProbabilityRandomRule neither implements IClientConfigAware nor provides a constructor with IClientConfig as the parameter. Only default constructor will be used.  22:52:29.035 [main] INFO com.netflix.loadbalancer.BaseLoadBalancer - Client: happybks-client instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=happybks-client,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null 22:52:29.111 [main] INFO com.netflix.loadbalancer.DynamicServerListLoadBalancer - Using serverListUpdater PollingServerListUpdater 22:52:29.145 [main] WARN com.netflix.client.ClientFactory - Class com.happybks.invokers.MyProbabilityRandomRule neither implements IClientConfigAware nor provides a constructor with IClientConfig as the parameter. Only default constructor will be used. 22:52:29.165 [main] INFO com.netflix.loadbalancer.DynamicServerListLoadBalancer - DynamicServerListLoadBalancer for client happybks-client initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=happybks-client,current list of Servers=[localhost:8091, localhost:8092],Load balancer stats=Zone stats: {unknown=[Zone:unknown; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:localhost:8092; Zone:UNKNOWN; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 GMT+08:00 1970; First connection made: Thu Jan 01 08:00:00 GMT+08:00 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] , [Server:localhost:8091; Zone:UNKNOWN; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 GMT+08:00 1970; First connection made: Thu Jan 01 08:00:00 GMT+08:00 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] ]}ServerList:com.netflix.loadbalancer.ConfigurationBasedServerList@152aa092 22:52:29.165 [main] INFO com.netflix.client.ClientFactory - Client: happybks-client instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=happybks-client,current list of Servers=[localhost:8091, localhost:8092],Load balancer stats=Zone stats: {unknown=[Zone:unknown; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:localhost:8092; Zone:UNKNOWN; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 GMT+08:00 1970; First connection made: Thu Jan 01 08:00:00 GMT+08:00 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] , [Server:localhost:8091; Zone:UNKNOWN; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 GMT+08:00 1970; First connection made: Thu Jan 01 08:00:00 GMT+08:00 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] ]}ServerList:com.netflix.loadbalancer.ConfigurationBasedServerList@152aa092 22:52:29.167 [main] INFO com.netflix.client.ClientFactory - Client Registered:com.netflix.niws.client.http.RestClient@44a7bfbc the 1th: 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.DynamicServerListLoadBalancer - List of Servers for happybks-client obtained from Discovery client: [localhost:8091, localhost:8092] 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.DynamicServerListLoadBalancer - Filtered List of Servers for happybks-client obtained from Discovery client: [localhost:8091, localhost:8092] 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [happybks-client]: clearing server list (SET op) 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [happybks-client]: addServer [localhost:8091] 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [happybks-client]: addServer [localhost:8092] 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.DynamicServerListLoadBalancer - Setting server list for zones: {unknown=[localhost:8091, localhost:8092]} 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [happybks-client_unknown]: clearing server list (SET op) 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [happybks-client_unknown]: addServer [localhost:8091] 22:52:30.115 [PollingServerListUpdater-0] DEBUG com.netflix.loadbalancer.BaseLoadBalancer - LoadBalancer [happybks-client_unknown]: addServer [localhost:8092] {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8092/carsInfo/onsale"} the 2th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8091/carsInfo/onsale"} the 3th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8091/carsInfo/onsale"} the 4th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8091/carsInfo/onsale"} the 5th: andName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8091/carsInfo/onsale"} the 6th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8092/carsInfo/onsale"} the 7th: andName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8092/carsInfo/onsale"} the 8th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8091/carsInfo/onsale"} the 9th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8092/carsInfo/onsale"} the 10th: {"brandName":"Volvo","price":536000.0,"serviceUrl":"http://localhost:8091/carsInfo/onsale"} 22:52:30.889 [Thread-2] INFO com.netflix.loadbalancer.PollingServerListUpdater - Shutting down the Executor Pool for PollingServerListUpdater 

從上面的結果可以看出,有6個請求為8091,剩下4個為8092,我們自己定義的規則的功能就實現了。

 

Ribbon內置負載均衡規則

Ribbon框架按照不同需求,已經為我們實現了許多實現了IRule接口的實現類,適用於常用的負載均衡規則。以下規則能夠實現大部分負載均衡需求的應用場景,如果有更復雜的需求,可以自己實現IRule。

內置負載均衡規則類 規則描述
RoundRobinRule 簡單輪詢服務列表來選擇服務器。它是Ribbon默認的負載均衡規則。
AvailabilityFilteringRule

對以下兩種服務器進行忽略:

(1)在默認情況下,這台服務器如果3次連接失敗,這台服務器就會被設置為“短路”狀態。短路狀態將持續30秒,如果再次連接失敗,短路的持續時間就會幾何級地增加。

注意:可以通過修改配置loadbalancer.<clientName>.connectionFailureCountThreshold來修改連接失敗多少次之后被設置為短路狀態。默認是3次。

(2)並發數過高的服務器。如果一個服務器的並發連接數過高,配置了AvailabilityFilteringRule規則的客戶端也會將其忽略。並發連接數的上線,可以由客戶端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit屬性進行配置。

 

 

WeightedResponseTimeRule

為每一個服務器賦予一個權重值。服務器響應時間越長,這個服務器的權重就越小。這個規則會隨機選擇服務器,這個權重值會影響服務器的選擇。

ZoneAvoidanceRule 以區域可用的服務器為基礎進行服務器的選擇。使用Zone對服務器進行分類,這個Zone可以理解為一個機房、一個機架等。
BestAvailableRule 忽略哪些短路的服務器,並選擇並發數較低的服務器。
RandomRule 隨機選擇一個可用的服務器。
Retry 重試機制的選擇邏輯

 

附錄:AvailabilityFilteringRule的三個默認配置

# successive connection failures threshold to put the server in circuit tripped state, default 3 niws.loadbalancer.<clientName>.connectionFailureCountThreshold # Maximal period that an instance can remain in "unusable" state regardless of the exponential increase, default 30 niws.loadbalancer.<clientName>.circuitTripMaxTimeoutSeconds # threshold of concurrent connections count to skip the server, default is Integer.MAX_INT <clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM