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


    前言

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

  • 相关阅读:
    JavaScript WebSocket C# SuperSocket.WebSocket 示例
    Nginx 配置
    Temporary Post Used For Theme Detection (272f6d70fb8946f3a568afd3d7b053bd 3bfe001a32de4114a6b44005b770f6d7)
    SpringBoot多数据源事务解决方案
    SpringBoot集成mybatis拦截器修改表名
    点击各个按钮,在执行操作之前出现确认提示框
    incoming change和current change
    Expected Number with value 8, got String with value "8".
    正则只能输入数字遇到的问题
    Antd 4.19 Modal + Form;打开modal时使用setFieldsValue,首次回显正常,第二次无效?
  • 原文地址:https://www.cnblogs.com/panzi/p/10651287.html
Copyright © 2020-2023  润新知