几行代码实现负载均衡轮询算法

前言

  负载均衡在架构设计中是经常提到的一种方案,用以提高系统处理量。今天用几句代码实现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解决,效率会高一些。也可以使用锁。

posted @ 2019-04-03 20:29  丶Pz  阅读(1232)  评论(0编辑  收藏  举报