springcloud ribbon 源碼分析(7)——spring cloud與ribbon整合時的默認負載均衡算法如何選擇一個server?

通過LoadBalancer從一個服務對應的server list中選擇一個server出來,保持負載均衡,將請求均勻的打到各個服務器上去

LoadBalancer的chooseServer()方法,通過自己內置的負載均衡算法,選擇一個server出來

ZoneAwareLoadBalancer,Zone根本不用管他,因爲我們這裏暫時沒有Zone的概念,機房的概念,多機房的話,他這裏可以感知到多機房的,將一個機房裏的請求,轉發給自己這個機房裏部署的其他的服務實例

 

(1)ZoneAwareLoadBalancer.chooseServer()方法,在這裏對服務的server list選擇了一個出來

內部,一定是對每個zone,對每個機房都搞了一個LoadBalancer,所以ZoneAwareLoadBalancer內部還是基於BaseLoadBalander在工作的,封裝了多個機房,對每個機房的請求,都找每個機房自己對應的一個BaseLoadBalancer,直接調用了BaseLoadBalancer的chooseServer()方法選擇了一個server出來

public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {

    @Override
    public Server chooseServer(Object key) {
        //區域感知邏輯禁用或只有一個區域zone
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            //直接調用了BaseLoadBalancer的chooseServer()方法選擇了一個server出來
            return super.chooseServer(key);
        }
        Server server = null;
        try {
            LoadBalancerStats lbStats = getLoadBalancerStats();
            Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
            logger.debug("Zone snapshots: {}", zoneSnapshot);
            if (triggeringLoad == null) {
                triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
            }

            if (triggeringBlackoutPercentage == null) {
                triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
            }
            Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
            logger.debug("Available zones: {}", availableZones);
            if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
                String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                logger.debug("Zone chosen: {}", zone);
                if (zone != null) {
                    BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
                    server = zoneLoadBalancer.chooseServer(key);
                }
            }
        } catch (Exception e) {
            logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
        }
        if (server != null) {
            return server;
        } else {
            logger.debug("Zone avoidance logic is not invoked.");
            return super.chooseServer(key);
        }
    }

 

(2)BaseLoadBalancer的chooseServer()方法中,直接就是用的IRule來選擇了一臺服務器

public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware {

    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                //直接就是用的IRule來選擇了一臺服務器
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

}

 

(3)用的是RibbonClientConfiguraiton中實例化的一個ZoneAvoidanceRule,調用了他的choose()方法來選擇一個server,其實是用的父類,PredicateBasedRule.choose()方法,先執行過濾規則,過濾掉一批server,根據你自己指定的filter規則,然後用round robin輪詢算法,依次獲取下一個server

public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
   
    /**
     * Method that provides an instance of {@link AbstractServerPredicate} to be used by this class.
     * 
     */
    public abstract AbstractServerPredicate getPredicate();
        
    /**
     * Get a server by calling {@link AbstractServerPredicate#chooseRandomlyAfterFiltering(java.util.List, Object)}.
     * The performance for this method is O(n) where n is number of servers to be filtered.
     */
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        //根據你自己指定的filter規則先執行過濾,過濾掉一批server,然後用round robin輪詢算法,依次獲取下一個server
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }
}

 

(4)調用AbstractServerPredicate的chooseRoundRobinAfterFiltering方法完成服務輪詢調度選取

輪詢算法其實就是把所有的server放到一個list中,然後用nextIndex.getAndIncrement() % eligible.size()取模依次返回list的索引值(0到eligible.size()-1),方法每執行一次nextIndex都會通過getAndIncrement方法累加1,從而實現依次返回不同的索引值,最終完成輪詢選擇server的效果。

public abstract class AbstractServerPredicate implements Predicate<PredicateKey> {

    private final AtomicInteger nextIndex = new AtomicInteger();

    /**
     * Choose a server in a round robin fashion after the predicate filters a given list of servers and load balancer key.
     * 在過濾後的server list中,用round robin輪詢算法,依次獲取下一個server
     */
    public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
        //獲取符合條件的服務列表,比如ServiceA一共對應了2臺服務實例(localhost:8080和localhost:8088)
        List<Server> eligible = getEligibleServers(servers, loadBalancerKey);
        if (eligible.size() == 0) {
            return Optional.absent();
        }
        //nextIndex.getAndIncrement() % eligible.size()爲一個簡單的輪詢算法獲取索引值
        return Optional.of(eligible.get(nextIndex.getAndIncrement() % eligible.size()));
    }
}

 

總結:spring cloud與ribbon整合時的一個默認的負載均衡算法 流程圖

 

 

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