美文网首页Java
Nginx负载均衡算法之加权轮询 java实现

Nginx负载均衡算法之加权轮询 java实现

作者: else05 | 来源:发表于2018-10-09 15:42 被阅读155次

    今天在看《大型网站技术架构》时,里面介绍负载均衡算法时提到加权轮询(Weighted Round Robin) , 对其具体实现不太熟悉,网上的介绍有点乱,不太好理解,所以这里自己实现一下这个算法,以自己能通俗理解的方式记录一下

    一. 加权轮询在nginx中的部分配置
    http {  
        upstream cluster {  
            server 192.168.1.2 weight=5;  
            server 192.168.1.3 weight=3;  
            server 192.168.1.4 weight=1;  
        }  
        ...
    location / {
           proxy_set_header X-Real-IP $remote_addr;               //返回真实IP
           proxy_pass http://cluster;                           //代理指向cluster 
       }
    

    二. 加权轮询算法介绍

    1. 由于不同服务器的配置、部署的应用不同,其处理能力会不一样。所以,加权轮询算法的原理就是:根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。
    2. Nginx加权轮询算法中,每个节点有三个权重变量
    • weight : 配置的权重,即在配置文件或初始化时约定好的每个节点的权重
    • currentWeight : 节点当前权重,会一直变化
    • effectiveWeight :有效权重,初始值为weight, 通讯过程中发现节点异常,则-1 ,之后再次选取本节点,调用成功一次则+1,直达恢复到weight 。 用于健康检查,处理异常节点,降低其权重。
    1. 算法逻辑
      (1) 轮询所有节点,计算当前状态下所有节点的effectiveWeight之和totalWeight
      (2) currentWeight = currentWeight + effectiveWeight; 选出所有节点中currentWeight中最大的一个节点作为选中节点;
      (3) 选中节点的currentWeight = currentWeight - totalWeight
      注:(为了简单清晰,后面的实现不考虑健康检查effectiveWeight这个功能实现,假设所有节点都是100%可用,所以上面的逻辑要把使用effectiveWeight的地方换成weight
    2. 例子
      假设有三个节点{serverA , serverB , serverC} , 权重为分别为{4,3,2} ,现在请求7次的分配情况如下:
    请求次数 请求前currentWeight 选中的节点 请求后currentWeight
    1 [ serverA= 4,serverB= 3,serverC= 2 ] serverA [ serverA= -1,serverB= 6,serverC= 4 ]
    2 [ serverA= -1,serverB= 6,serverC= 4 ] serverB [ serverA= 3,serverB= 0,serverC= 6 ]
    3 [ serverA= 3,serverB= 0,serverC= 6 ] serverC [ serverA= 7,serverB= 3,serverC= -1 ]
    4 [ serverA= 7,serverB=3,serverC= -1 ] serverA [ serverA= 2,serverB= 6,serverC= 1 ]
    5 [ serverA= 2,serverB= 6,serverC= 1 ] serverB [ serverA= 6,serverB= 0,serverC= 3 ]
    6 [ serverA= 6,serverB= 0,serverC= 3 ] serverA [ serverA= 1,serverB= 3,serverC= 5 ]
    7 [ serverA= 1,serverB= 3,serverC= 5 ] serverC [ serverA= 5,serverB= 6,serverC= -2 ]

    totaoWeight = 4 + 3 + 2 = 9 ,
    第一次请求 serverA= 4 + 4(原始比重) = 8 ,serverB= 3+3(原始比重) = 6,serverC= 2+2(原始比重) = 4 , 最大的是serverA , 所以 serverA= 8 - 9 (权重总和) = -1 ,所以最后值为 serverA= -1,serverB= 6,serverC= 4
    第二次请求 serverA= -1 + 4(原始比重) = 3 ,serverB= 6+3(原始比重) = 9,serverC= 4+2(原始比重) = 6 , 最大的是serverB , 所以 serverB= 9 - 9 (权重总和) = 0 ,所以最后值为 serverA= 3,serverB= 0,serverC= 6
    依此类推...

    三. 代码实现(java)

    • DemoApplication main方法
    package com.example.demo;
    
    public class DemoApplication {
        public static void main(String[] args) {
            /**
             * 假设有三个服务器权重配置如下:
             * server A  weight = 4 ;
             * server B  weight = 3 ;
             * server C  weight = 2 ;
             */
            Node serverA = new Node("serverA", 4);
            Node serverB = new Node("serverB", 3);
            Node serverC = new Node("serverC", 2);
            
            SmoothWeightedRoundRobin smoothWeightedRoundRobin = new SmoothWeightedRoundRobin(serverA,serverB ,serverC);
            for (int i = 0; i < 7; i++) {
                Node i1 = smoothWeightedRoundRobin.select();
                System.out.println(i1.getServerName());
            }
        }
    }
    
    • SmoothWeightedRoundRobin 加权轮询算法
    package com.example.demo;
    
    import java.util.*;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class SmoothWeightedRoundRobin {
        private volatile List<Node> nodeList = new ArrayList<>() ; // 保存权重
        private ReentrantLock lock = new ReentrantLock() ;
    
        public SmoothWeightedRoundRobin(Node ...nodes) {
            for (Node node : nodes) {
                nodeList.add(node) ;
            }
        }
    
        public Node select(){
            try {
                lock.lock();
                return this.selectInner() ;
            }finally {
                lock.unlock();
            }
        }
    
        private Node selectInner(){
            int totalWeight = 0 ;
            Node maxNode = null ;
            int maxWeight = 0 ;
    
            for (int i = 0; i < nodeList.size(); i++) {
                Node n = nodeList.get(i);
                totalWeight += n.getWeight() ;
    
                // 每个节点的当前权重要加上原始的权重
                n.setCurrentWeight(n.getCurrentWeight() + n.getWeight());
    
                // 保存当前权重最大的节点
                if (maxNode == null || maxWeight < n.getCurrentWeight() ) {
                    maxNode = n ;
                    maxWeight = n.getCurrentWeight() ;
                }
            }
            // 被选中的节点权重减掉总权重
            maxNode.setCurrentWeight(maxNode.getCurrentWeight() - totalWeight);
    
    //        nodeList.forEach(node -> System.out.print(node.getCurrentWeight()));
            return maxNode ;
        }
    }
    
    • Node 服务节点
    package com.example.demo;
    
    public class Node {
        private final int weight ;  // 初始权重 (保持不变)
        private final String serverName ; // 服务名
    
        private int currentWeight ; // 当前权重
    
        public Node( String serverName, int weight) {
            this.weight = weight;
            this.serverName = serverName ;
            this.currentWeight = weight ;
        }
    
        public int getCurrentWeight() {
            return currentWeight;
        }
    
        public int getWeight() {
            return weight;
        }
    
        public void setCurrentWeight(int currentWeight) {
            this.currentWeight = currentWeight;
        }
    
        public String getServerName() {
            return serverName;
        }
    }
    

    这个算法相对保证了的平滑性和均匀性

    相关文章

      网友评论

        本文标题:Nginx负载均衡算法之加权轮询 java实现

        本文链接:https://www.haomeiwen.com/subject/hpweaftx.html