Ribbon自定義負載均衡策略有兩種方式,一是JavaConfig,一是經過配置文件(yml或properties文件)。html
假設我有包含A和B服務在內的多個微服務,它們均註冊在一個Eureka上,信息以下:
java
我但願當訪問服務A時候,2個服務(端口分別是8087和8081)每兩次一換,好比訪問兩次8087,再訪問兩次8081,如此反覆。
當訪問服務B時,與A相似,不過是3次一換。
當訪問其餘服務時,採用隨機規則,即RandomRule,而不是默認策略1web
使用這種方式,總共分3步(以服務A的規則爲例):spring
@Primary
2,重寫邏輯需要根據實際需求來定,詳見下面的代碼IRule
的Bean,此處咱們選RandomRule
@RibbonClients
(只有一種自定義規則,則使用@RibbonClient
),添加不適用公共規則的其餘自定義規則import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import org.springframework.context.annotation.Primary; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @Primary public class SvcARule extends AbstractLoadBalancerRule { private AtomicInteger total = new AtomicInteger(); private AtomicInteger index = new AtomicInteger(); public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } if (total.get() < 2) { server = upList.get(index.get()); total.incrementAndGet(); } else { total.set(0); index.set(index.incrementAndGet() % upList.size()); } if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } server = null; Thread.yield(); } return server; } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub } }
略,只須要把if (total.get() < 2)
改爲if (total.get() < 3)
便可服務器
import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import com.yan.ribbon.rule.SvcARule; import com.yan.ribbon.rule.SvcBRule; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @RibbonClients( value = { @RibbonClient(name = "SVCA", configuration = SvcARule.class), @RibbonClient(name = "SVCB", configuration = SvcBRule.class) }, defaultConfiguration = LBConfig.class) public class LBConfig { @Bean public IRule commonRule() { return new RandomRule(); } }
經過postman屢次訪問服務A、B和C,獲得日誌以下:併發
2019-03-19 20:22:09.511 INFO 13372 --- [nio-8004-exec-4] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-03-19 20:22:09.511 INFO 13372 --- [nio-8004-exec-4] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-03-19 20:22:09.519 INFO 13372 --- [nio-8004-exec-4] o.s.web.servlet.DispatcherServlet : Completed initialization in 8 ms 2019-03-19 20:22:09.693 INFO 13372 --- [nio-8004-exec-4] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-SVCA 2019-03-19 20:22:09.708 INFO 13372 --- [nio-8004-exec-4] c.netflix.loadbalancer.BaseLoadBalancer : Client: SVCA instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCA,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null 2019-03-19 20:22:09.712 INFO 13372 --- [nio-8004-exec-4] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater 2019-03-19 20:22:09.729 INFO 13372 --- [nio-8004-exec-4] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client SVCA initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCA,current list of Servers=[YanWei:8087, YanWei:8081],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:YanWei:8087; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 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:YanWei:8081; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 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:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@7f0246c6 "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" 2019-03-19 20:22:37.173 INFO 13372 --- [nio-8004-exec-5] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-SVCB 2019-03-19 20:22:37.174 INFO 13372 --- [nio-8004-exec-5] c.netflix.loadbalancer.BaseLoadBalancer : Client: SVCB instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCB,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null 2019-03-19 20:22:37.177 INFO 13372 --- [nio-8004-exec-5] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater 2019-03-19 20:22:37.178 INFO 13372 --- [nio-8004-exec-5] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client SVCB initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCB,current list of Servers=[YanWei:8086, YanWei:8082],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:YanWei:8082; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 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:YanWei:8086; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 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:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@485ed210 "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8082===>\nname:xixi\ntype:haha" "svcB:8082===>\nname:xixi\ntype:haha" "svcB:8082===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8082===>\nname:xixi\ntype:haha" "svcB:8082===>\nname:xixi\ntype:haha" "svcB:8082===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" "svcB:8086===>\nname:xixi\ntype:haha" 2019-03-19 20:23:01.319 INFO 13372 --- [nio-8004-exec-8] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-SVCC 2019-03-19 20:23:01.320 INFO 13372 --- [nio-8004-exec-8] c.netflix.loadbalancer.BaseLoadBalancer : Client: SVCC instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCC,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null 2019-03-19 20:23:01.320 INFO 13372 --- [nio-8004-exec-8] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater 2019-03-19 20:23:01.322 INFO 13372 --- [nio-8004-exec-8] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client SVCC initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=SVCC,current list of Servers=[YanWei:8083, YanWei:8085],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:YanWei:8083; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 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:YanWei:8085; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 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:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@77b35c9 "svcC:8085===>\nname:xixi\ntype:haha" "svcC:8083===>\nname:xixi\ntype:haha" "svcC:8085===>\nname:xixi\ntype:haha" "svcC:8085===>\nname:xixi\ntype:haha" "svcC:8083===>\nname:xixi\ntype:haha" "svcC:8085===>\nname:xixi\ntype:haha" "svcC:8085===>\nname:xixi\ntype:haha"
配置文件方式需要遵照如下格式:app
<serviceName>.ribbon.<key>=<value>
幾個變量解釋以下:負載均衡
serviceName
就是服務名
key
是指Ribbon爲實現不一樣領域(咱們這裏只說IRule,其實還有IPing等)所指定的關聯類名,這裏我須要自定義IRule
,其關聯類名,也就是所謂的key
,是NFLoadBalancerRuleClassName
3
value
是我所要指定的自定義規則,好比,SvcB的負載均衡策略SvcBRule
dom
所以,在配置文件中添加以下配置:ide
svcB: ribbon: NFLoadBalancerRuleClassName: com.yan.ribbon.rule.SvcBRule
從新啓動Ribbon服務後,發現此配置並無生效,緣由有兩個:
一是個人服務名字寫的是svcB
,理由是個人配置spring.appliaction.name=svcB
,可是使用Ribbon調用時使用了http://SVCB/...
,SVCB是大寫的,因此沒有獲得指望的效果4
二是我已經有了用來替換默認規則的LBConfig,使得IRule的默認配置沒有生效,由於查看源碼org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
中對於IRule的定義以下:
@Bean @ConditionalOnMissingBean public IRule ribbonRule(IClientConfig config) { if (this.propertiesFactory.isSet(IRule.class, name)) { return this.propertiesFactory.get(IRule.class, config, name); } ZoneAvoidanceRule rule = new ZoneAvoidanceRule(); rule.initWithNiwsConfig(config); return rule; }
因爲我自定義的commonRule
已經存在了,因此此處不會觸發,更不會走到判斷那一步,就是說,個人上面的關於SvcB的自定義規則的配置,是沒有什麼地方去讀的,所以,爲了方便演示,直接將LBConfig所有注掉,再次重啓後,把訪問Uri中的大寫改爲一致的svcB
,發現對於服務B的配置生效了,可是沒有指定的服務,所有都採用了默認的策略ZoneAvoidanceRule
。
配置文件方式,比較麻煩,且不能-不必也懶得研究如何-修改默認規則,而JavaConfig能夠完美替代,所以我選JavaConfig。
關於Ribbon中對於IRule的實現,總共有以下幾種,其簡略說明也附註以下:
對應類爲ZoneAvoidanceRule,直譯過來是區域迴避規則,我也不知道爲啥這樣叫:D↩
若是不加此註解,會報錯,內容是找類型爲IRule的Bean,可是找到了倆,一個commonRule,一個svcARule。若是你不嫌麻煩,能夠選擇網上流行的較爲複雜的形式,好比springcloud-04-自定義ribbon的配置方式↩
能夠在類org.springframework.cloud.netflix.ribbon.PropertiesFactory
中查看↩
這個Eureka也要背鍋,由於使用服務名來進行服務間調用,是被大小寫是被兼容過的,致使我這邊疏忽大意了,仍是要細心的好。↩