您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Ribbon的負(fù)載均衡策略及原理是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
Load Balance負(fù)載均衡是用于解決一臺(tái)機(jī)器(一個(gè)進(jìn)程)無(wú)法解決所有請(qǐng)求而產(chǎn)生的一種算法。像nginx可以使用負(fù)載均衡分配流量,ribbon為客戶端提供負(fù)載均衡,dubbo服務(wù)調(diào)用里的負(fù)載均衡等等,很多地方都使用到了負(fù)載均衡。
使用負(fù)載均衡帶來(lái)的好處很明顯:
當(dāng)集群里的1臺(tái)或者多臺(tái)服務(wù)器down的時(shí)候,剩余的沒(méi)有down的服務(wù)器可以保證服務(wù)的繼續(xù)使用
使用了更多的機(jī)器保證了機(jī)器的良性使用,不會(huì)由于某一高峰時(shí)刻導(dǎo)致系統(tǒng)cpu急劇上升
負(fù)載均衡有好幾種實(shí)現(xiàn)策略,常見(jiàn)的有:
隨機(jī) (Random)
輪詢 (RoundRobin)
一致性哈希 (ConsistentHash)
哈希 (Hash)
加權(quán)(Weighted)
ILoadBalance 負(fù)載均衡器
ribbon是一個(gè)為客戶端提供負(fù)載均衡功能的服務(wù),它內(nèi)部提供了一個(gè)叫做ILoadBalance的接口代表負(fù)載均衡器的操作,比如有添加服務(wù)器操作、選擇服務(wù)器操作、獲取所有的服務(wù)器列表、獲取可用的服務(wù)器列表等等。ILoadBalance的繼承關(guān)系如下:
負(fù)載均衡器是從EurekaClient(EurekaClient的實(shí)現(xiàn)類為DiscoveryClient)獲取服務(wù)信息,根據(jù)IRule去路由,并且根據(jù)IPing判斷服務(wù)的可用性。
負(fù)載均衡器多久一次去獲取一次從Eureka Client獲取注冊(cè)信息呢?在BaseLoadBalancer類下,BaseLoadBalancer的構(gòu)造函數(shù),該構(gòu)造函數(shù)開(kāi)啟了一個(gè)PingTask任務(wù)setupPingTask();,代碼如下:
public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats, IPing ping, IPingStrategy pingStrategy) { if (logger.isDebugEnabled()) { logger.debug("LoadBalancer: initialized"); } this.name = name; this.ping = ping; this.pingStrategy = pingStrategy; setRule(rule); setupPingTask(); lbStats = stats; init(); }
setupPingTask()的具體代碼邏輯,它開(kāi)啟了ShutdownEnabledTimer執(zhí)行PingTask任務(wù),在默認(rèn)情況下pingIntervalSeconds為10,即每10秒鐘,向EurekaClient發(fā)送一次”ping”。
void setupPingTask() { if (canSkipPing()) { return; } if (lbTimer != null) { lbTimer.cancel(); } lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-">
PingTask源碼,即new一個(gè)Pinger對(duì)象,并執(zhí)行runPinger()方法。
查看Pinger的runPinger()方法,最終根據(jù) pingerStrategy.pingServers(ping, allServers)來(lái)獲取服務(wù)的可用性,如果該返回結(jié)果,如之前相同,則不去向EurekaClient獲取注冊(cè)列表,如果不同則通知ServerStatusChangeListener或者changeListeners發(fā)生了改變,進(jìn)行更新或者重新拉取。
完整過(guò)程是:
LoadBalancerClient(RibbonLoadBalancerClient是實(shí)現(xiàn)類)在初始化的時(shí)候(execute方法),會(huì)通過(guò)ILoadBalance(BaseLoadBalancer是實(shí)現(xiàn)類)向Eureka注冊(cè)中心獲取服務(wù)注冊(cè)列表,并且每10s一次向EurekaClient發(fā)送“ping”,來(lái)判斷服務(wù)的可用性,如果服務(wù)的可用性發(fā)生了改變或者服務(wù)數(shù)量和之前的不一致,則從注冊(cè)中心更新或者重新拉取。LoadBalancerClient有了這些服務(wù)注冊(cè)列表,就可以根據(jù)具體的IRule來(lái)進(jìn)行負(fù)載均衡。
IRule 路由
IRule接口代表負(fù)載均衡策略:
public interface IRule{ public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }
IRule接口的實(shí)現(xiàn)類有以下幾種:
其中RandomRule表示隨機(jī)策略、RoundRobinRule表示輪詢策略、WeightedResponseTimeRule表示加權(quán)策略、BestAvailableRule表示請(qǐng)求數(shù)最少策略等等。
隨機(jī)策略很簡(jiǎn)單,就是從服務(wù)器中隨機(jī)選擇一個(gè)服務(wù)器,RandomRule的實(shí)現(xiàn)代碼如下:
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; } int index = rand.nextInt(serverCount); // 使用jdk內(nèi)部的Random類隨機(jī)獲取索引值index server = upList.get(index); // 得到服務(wù)器實(shí)例 if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } server = null; Thread.yield(); } return server; }
RoundRobinRule輪詢策略表示每次都取下一個(gè)服務(wù)器,比如一共有5臺(tái)服務(wù)器,第1次取第1臺(tái),第2次取第2臺(tái),第3次取第3臺(tái),以此類推:
public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; int count = 0; while (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { log.warn("No up servers available from load balancer: ">
WeightedResponseTimeRule繼承了RoundRobinRule,開(kāi)始的時(shí)候還沒(méi)有權(quán)重列表,采用父類的輪詢方式,有一個(gè)默認(rèn)每30秒更新一次權(quán)重列表的定時(shí)任務(wù),該定時(shí)任務(wù)會(huì)根據(jù)實(shí)例的響應(yīng)時(shí)間來(lái)更新權(quán)重列表,choose方法做的事情就是,用一個(gè)(0,1)的隨機(jī)double數(shù)乘以最大的權(quán)重得到randomWeight,然后遍歷權(quán)重列表,找出第一個(gè)比randomWeight大的實(shí)例下標(biāo),然后返回該實(shí)例,代碼略。
BestAvailableRule策略用來(lái)選取最少并發(fā)量請(qǐng)求的服務(wù)器:
public Server choose(Object key) { if (loadBalancerStats == null) { return super.choose(key); } List<Server> serverList = getLoadBalancer().getAllServers(); // 獲取所有的服務(wù)器列表 int minimalConcurrentConnections = Integer.MAX_VALUE; long currentTime = System.currentTimeMillis(); Server chosen = null; for (Server server: serverList) { // 遍歷每個(gè)服務(wù)器 ServerStats serverStats = loadBalancerStats.getSingleServerStat(server); // 獲取各個(gè)服務(wù)器的狀態(tài) if (!serverStats.isCircuitBreakerTripped(currentTime)) { // 沒(méi)有觸發(fā)斷路器的話繼續(xù)執(zhí)行 int concurrentConnections = serverStats.getActiveRequestsCount(currentTime); // 獲取當(dāng)前服務(wù)器的請(qǐng)求個(gè)數(shù) if (concurrentConnections < minimalConcurrentConnections) { // 比較各個(gè)服務(wù)器之間的請(qǐng)求數(shù),然后選取請(qǐng)求數(shù)最少的服務(wù)器并放到chosen變量中 minimalConcurrentConnections = concurrentConnections; chosen = server; } } } if (chosen == null) { // 如果沒(méi)有選上,調(diào)用父類ClientConfigEnabledRoundRobinRule的choose方法,也就是使用RoundRobinRule輪詢的方式進(jìn)行負(fù)載均衡 return super.choose(key); } else { return chosen; } }
使用Ribbon提供的負(fù)載均衡策略很簡(jiǎn)單,只需以下幾部:
1、創(chuàng)建具有負(fù)載均衡功能的RestTemplate實(shí)例
@Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); }
使用RestTemplate進(jìn)行rest操作的時(shí)候,會(huì)自動(dòng)使用負(fù)載均衡策略,它內(nèi)部會(huì)在RestTemplate中加入LoadBalancerInterceptor這個(gè)攔截器,這個(gè)攔截器的作用就是使用負(fù)載均衡。
默認(rèn)情況下會(huì)采用輪詢策略,如果希望采用其它策略,則指定IRule實(shí)現(xiàn),如:
@Bean public IRule ribbonRule() { return new BestAvailableRule(); }
這種方式對(duì)Feign也有效。
我們也可以參考ribbon,自己寫(xiě)一個(gè)負(fù)載均衡實(shí)現(xiàn)類。
可以通過(guò)下面方法獲取負(fù)載均衡策略最終選擇了哪個(gè)服務(wù)實(shí)例:
@Autowired LoadBalancerClient loadBalancerClient; //測(cè)試負(fù)載均衡最終選中哪個(gè)實(shí)例 public String getChoosedService() { ServiceInstance serviceInstance = loadBalancerClient.choose("USERINFO-SERVICE"); StringBuilder sb = new StringBuilder(); sb.append("host: ").append(serviceInstance.getHost()).append(", "); sb.append("port: ").append(serviceInstance.getPort()).append(", "); sb.append("uri: ").append(serviceInstance.getUri()); return sb.toString(); }
“Ribbon的負(fù)載均衡策略及原理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。