幾行程式碼實現負載均衡輪詢演算法

丶Pz發表於2019-04-03

前言

  負載均衡在架構設計中是經常提到的一種方案,用以提高系統處理量。今天用幾句程式碼實現Round Robin方式,用白話文說就是有活大家輪著幹。在看了Ribbion原始碼之後,確實是幾行程式碼。

實現思路

  • 首先,要有一組服務列表
    private List<String> serverList = new LinkedList<>();

    public RoundRibbon() {
        serverList.add("http://server.01");
        serverList.add("http://server.02");
        serverList.add("http://server.03");
        serverList.add("http://server.04");
        serverList.add("http://server.05");
    }
  • 然後要有一個全域性的索引變數,然後通過取餘的方式計算下一個服務索引:
    int nextServerIndex = (currentIndex  + 1) % serverList.size();
  • 上面的程式碼在實戰中會有執行緒安全的問題,因此可以採用 AtomicInteger 實現。

生產實現

  在 Netflix/ribbon 的 RoundRobinRule 中實現程式碼如下:

 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) {
             //獲取可用的server列表
            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: " + lb);
                return null;
            }
            //核心實現,獲取server索引
            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }
            //是這種 
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }

            // Next.
            server = null;
        }
        //重試十次之後,沒有獲取到可用的服務,警告日誌
        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    /**
     * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
     *
     * @param modulo The modulo to bound the value of the counter.
     * @return The next value.
     */
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            //獲取當前的服務索引值
            int current = nextServerCyclicCounter.get();
            //通過取餘的方式計算下一個索引值
            int next = (current + 1) % modulo;
            //通過 CAS 設定下一個搜尋引值(解決併發索引值可能重複的問題)
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

總結

輪詢方式實現很簡單,關鍵難點就是解決執行緒安全問題。例子中通過使用CAS解決,效率會高一些。也可以使用鎖。

相關文章