Ribbon源码解析(二)
目錄
ServerListFilter
AbstractServerListFilter
ZoneAffinityServerListFilter
ServerListSubsetFilter
ZonePreferenceServerListFilter
ServerListUpdater
IRule
AbstractLoadBalancerRule
RoundRobinRule?
WeightedResponseTimeRule 加權輪詢
ClientConfigEnabledRoundRobinRule
BestAvailableRule 最小并發數規則
PredicateBasedRule 基于斷言器規則
AvailabilityFilteringRule 可用性過濾規則
ZoneAvoidanceRule 可用區規則
RandomRule 隨機規則
RetryRule 重試規則
IRule所有內置規則
ServerListFilter
該接口用于過濾Server列表,返回可使用服務。
public interface ServerListFilter<T extends Server> {public List<T> getFilteredListOfServers(List<T> servers); }AbstractServerListFilter
規定了Server來源:來自于負載均衡器LB,這種可用/不可用是通過指標收集庫/存儲庫LoadBalancerStats計算出來的。
public abstract class AbstractServerListFilter<T extends Server> implements ServerListFilter<T> {private volatile LoadBalancerStats stats; }ZoneAffinityServerListFilter
它借助于ZoneAffinityPredicate來過濾出和zone相關的服務器。
@Overridepublic List<T> getFilteredListOfServers(List<T> servers) {if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){List<T> filteredServers = Lists.newArrayList(Iterables.filter(servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));if (shouldEnableZoneAffinity(filteredServers)) {return filteredServers;} else if (zoneAffinity) {overrideCounter.increment();}}return servers;}過濾邏輯中,最重要的乃shouldEnableZoneAffinity()這個方法,
- true:最終只留下本zone的Server列表
- false,返回所有Server,相當于忽略此Filter的操作
具體參數見:
次數使用的過濾斷言器是ZoneAffinityPredicate
ServerListSubsetFilter
一種服務器列表篩選器實現。它將負載均衡器使用的Server數量限制為所有服務器的子集。由于服務器比較多的情況。
因為全部返回出去,比如上千臺,那么都需要保留其httpclient鏈接在連接池中,挺耗資源的
在server list非常多的場景下,沒有必要在連接池的保持這么多的連接,ServerListSubsetFilter可以在這種場景下對server [list進行精簡,通過剔除相對不健康(failureCount、activeRequestCount)的server來達到此目標。
ZonePreferenceServerListFilter
它是Spring Cloud默認使用的篩選器。它的特點是:能夠優先過濾出與請求調用方處于同區域的服務實例。
ServerListUpdater
定時觸發相應的動作,被DynamicServerListLoadBalancer用于動態的更新服務列表。
public interface ServerListUpdater {public interface UpdateAction {void doUpdate();}// 使用給定的更新操作啟動serverList更新程序這個調用應該是冪等的void start(UpdateAction updateAction);// 停止服務器列表更新程序。這個調用應該是冪等的void stop();// 最后更新的時間Date的String表示形式String getLastUpdate();// 自上次更新以來已經過的ms數long getDurationSinceLastUpdateMs();//錯過更新周期的數量(如果有的話)int getNumberMissedCycles();// 使用的線程數int getCoreThreads(); }IRule
為LoadBalancer定義“規則”的接口,它是負載均衡算法的實現。負載均衡策略,根據特定算法中從服務列表中選取一個要訪問的Server。
public interface IRule {// 最為重要的一個方法:從lb.allServers/lb.upServers根據key找到一臺Server// 若沒找到返回nullpublic Server choose(Object key);public void setLoadBalancer(ILoadBalancer lb);public ILoadBalancer getLoadBalancer(); }AbstractLoadBalancerRule
該抽象類僅事把IRule和ILoadBalancer完成綁定,所以根據Rule選擇的Server列表均來自ILoadBalancer內。
public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {private ILoadBalancer lb; @Overridepublic void setLoadBalancer(ILoadBalancer lb){this.lb = lb;}@Overridepublic ILoadBalancer getLoadBalancer(){return lb;} }RoundRobinRule?
線性輪詢,計數加1。輪詢所有server列表。假設所有機器性能相同。
WeightedResponseTimeRule 加權輪詢
它繼承自輪詢算法,相較于它,增加了響應時間作為權重,為每個Server動態分配權重,然后按照權重輪詢(加權循環)。
當沒有為服務器收集足夠的統計信息時,此規則將回退到RoundRobinRule。所以他是依賴于LoadBalancerStats統計數據的。
ClientConfigEnabledRoundRobinRule
它的規則是可以通過ClientConfig來配置的,并非是固定的。它選擇策略的實現很簡單,內部定義了RoundRobinRule,choose方法還是采用了RoundRobinRule的choose方法,所以它的選擇策略和RoundRobinRule的選擇策略一致。
public class ClientConfigEnabledRoundRobinRule extends AbstractLoadBalancerRule {RoundRobinRule roundRobinRule = new RoundRobinRule();@Overridepublic Server choose(Object key) {if (roundRobinRule != null) {return roundRobinRule.choose(key);} else {throw new IllegalArgumentException("This class has not been initialized with the RoundRobinRule class");}} }BestAvailableRule 最小并發數規則
該策略的特性跳過已經被熔斷的實例,并且順表找出最空閑(也就是并發請求數最低的)的實例。統計數據來自云LoadBalancerStats / ServerStats。
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {// 也就是說:LoadBalancerStats是來自于lb的private LoadBalancerStats loadBalancerStats;@Overridepublic void setLoadBalancer(ILoadBalancer lb) {super.setLoadBalancer(lb);if (lb instanceof AbstractLoadBalancer) {loadBalancerStats = ((AbstractLoadBalancer) lb).getLoadBalancerStats(); }} }?
@Overridepublic Server choose(Object key) {// 若沒有統計信息,就回退到輪詢策略if (loadBalancerStats == null) {return super.choose(key);}List<Server> serverList = getLoadBalancer().getAllServers();// 記錄所有Server中最小并發數int minimalConcurrentConnections = Integer.MAX_VALUE;long currentTime = System.currentTimeMillis();Server chosen = null;for (Server server: serverList) {ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);// 只要該Server沒有被熔斷,就選擇上if (!serverStats.isCircuitBreakerTripped(currentTime)) {int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);if (concurrentConnections < minimalConcurrentConnections) {minimalConcurrentConnections = concurrentConnections;chosen = server;}}}if (chosen == null) {return super.choose(key);} else {return chosen;}}PredicateBasedRule 基于斷言器規則
基于斷言器AbstractServerPredicate來實現Server的篩選。規則:
- 先通過內部指定的一個AbstractServerPredicate斷言器過濾產生一個ServerList
- 然后再采用線性輪詢的方式從中選取一個服務實例
AvailabilityFilteringRule 可用性過濾規則
它依賴于AvailabilityPredicate完成過濾后,在使用線性輪詢方式選擇Server。它的choose邏輯是:
- 先輪詢選一臺Server出來,交給predicate去判斷是否合格(沒有被熔斷,且活躍連接數沒超過閾值才算合格),若合格就直接返回,否則重復此動作一共重復10次
它能實現故障實例的自動T除,這個特點在大規模集群下特別實用
- 若10次都還沒找到合格的,那就調用父類兜底getPredicate().chooseRoundRobinAfterFiltering()去輪詢一臺出來。
沒有使用父類的先過濾再輪詢,而是先輪詢再過濾。對于很多服務器的情況提高性能。
public class AvailabilityFilteringRule extends PredicateBasedRule {private AbstractServerPredicate predicate;public AvailabilityFilteringRule() {super();predicate = CompositePredicate.withPredicate(new AvailabilityPredicate(this, null)).addFallbackPredicate(AbstractServerPredicate.alwaysTrue()).build();}@Overridepublic AbstractServerPredicate getPredicate() {return predicate;}...@Overridepublic Server choose(Object key) {int count = 0;Server server = roundRobinRule.choose(key);//嘗試10次while (count++ <= 10) {if (predicate.apply(new PredicateKey(server))) {return server;}server = roundRobinRule.choose(key);}return super.choose(key);}}ZoneAvoidanceRule 可用區規則
使用的是一個CompositePredicate的組合過濾器:
private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {return CompositePredicate.withPredicates(p1, p2).addFallbackPredicate(p2).addFallbackPredicate(AbstractServerPredicate.alwaysTrue()).build();}RandomRule 隨機規則
隨機選擇一個server。使用ThreadLocalRandom.current().nextInt(serverCount);隨機來一個。幾乎不再使用。
public class RandomRule extends AbstractLoadBalancerRule {public Server choose(ILoadBalancer lb, Object key) {...Server server = null;while (server == null) {...List<Server> upList = lb.getReachableServers();List<Server> allList = lb.getAllServers();int serverCount = allList.size();int index = chooseRandomInt(serverCount);server = upList.get(index);...}return server;}protected int chooseRandomInt(int serverCount) {return ThreadLocalRandom.current().nextInt(serverCount);}}隨機器選擇的是ThreadLocalRandom,用于多線程。
BUG:
隨機數是通過allList.size(),也就是all所有Server的size,但是,取值的時候卻用upList.get(index)來取值。
RetryRule 重試規則
它可以在給定的任何IRule的基礎上再包一層重試邏輯(默認給定的RoundRobinRule規則)。RetryRule中又定義了一個subRule,它默認的實現類是RoundRobinRule(你可以自己指定任何IRule實現),每次先采用subRule#choose()規則來選擇一個服務實例,如果選到的實例正常就返回不需要重試;如果選擇的服務實例為null或者已經失效,則在失效時間deadline之前不斷的進行重試(重試時獲取服務的策略還是subRule#choose()來選擇),如果超過了deadline還是沒取到則會返回一個null。
// 選擇方法public Server choose(ILoadBalancer lb, Object key) {// 計算出一個開始選擇 和 結束選擇的時間long requestTime = System.currentTimeMillis();long deadline = requestTime + maxRetryMillis;// 先通過subRule選擇出一個serverServer answer = null;answer = subRule.choose(key);// 如果選擇出的Server為null,或者不是活的// 并且還在結束時間之前,就執行重試策略if (((answer == null) || (!answer.isAlive())) && (System.currentTimeMillis() < deadline)) {InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());while (!Thread.interrupted()) {... // 持續不斷的調用subRule.choose(key),直到獲取到或者時間到了}task.cancel();}// 如果最終還是沒獲取到可用的,那就返回null。否則返回正常結果if ((answer == null) || (!answer.isAlive())) {return null;} else {return answer;}}?
因為它屬于包裝器模式的一種實現,因此在實際生產中,推薦使用它來包裝實際的IRule,這樣會使得實例Server更加的健康,對網絡波動的容忍度更高些,RetryRule配合上RoundRobinRule的組合(也就是默認組合)效果很好:因為RoundRobinRule失效的策略是超過10次,而如果再配合上RetryRule,容錯性就會更強。
IRule所有內置規則
| RoundRobinRule | - | 線性輪詢 | 輪詢index,選擇index對應位置的server |
| WeightedResponseTimeRule | RoundRobinRule | 根據rt分配一個權重值,rt時間越長,weight越小,被選中的可能性就越低 | 使用一個后臺線程默認每30s重新計算一次權重值 |
| BestAvailableRule | ClientConfigEnabled… | 選擇一個活躍請求數最小的Server | 忽略已經被熔斷的Server |
| PredicateBasedRule | ClientConfigEnabled… | 基于斷言器實現的規則 | 本類為抽象類,具體過濾規則交給子類 |
| AvailabilityFilteringRule | PredicateBasedRule | 過濾掉已熔斷or活躍請求數太高的Server后,剩下的執行線性輪詢 | 依賴于AvailabilityPredicate這個斷言器實現過濾 |
| ZoneAvoidanceRule | PredicateBasedRule | 復合判斷。先選出可用區,然后在按上規則篩選出復合條件的Server們,執行線性輪詢 | 使用ZoneAvoidancePredicate和AvailabilityPredicate兩個主斷言器實現過濾 |
| RandomRule | - | 完全隨機選擇 | 此實現有bug,有bug,有bug |
| RetryRule | - | 對任何IRule包一層重試機制 | 在一個配置時間段內當選擇server不成功,則一直嘗試使用subRule的方式選擇一個可用的server |
?
總結
以上是生活随笔為你收集整理的Ribbon源码解析(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ribbon源码解析(一)
- 下一篇: Feign深入学习(二)