当前位置 博文首页 > 外星喵的博客:负载均衡算法

    外星喵的博客:负载均衡算法

    作者:[db:作者] 时间:2021-07-12 15:42

    负载均衡介绍

    负载均衡(Load Balance,简称 LB)是高并发、高可用系统必不可少的关键组件,目标是 尽力将网络流量平均分发到多个服务器上,以提高系统整体的响应速度和可用性。

    负载均衡的主要作用如下:

    • 高并发:负载均衡通过算法调整负载,尽力均匀的分配应用集群中各节点的工作量,以此提高应用集群的并发处理能力(吞吐量)。
    • 伸缩性:添加或减少服务器数量,然后由负载均衡进行分发控制。这使得应用集群具备伸缩性。
    • 高可用:负载均衡器可以监控候选服务器,当服务器不可用时,自动跳过,将请求分发给可用的服务器。这使得应用集群具备高可用的特性。
    • 安全防护:有些负载均衡软件或硬件提供了安全性功能,如:黑白名单处理、防火墙,防 DDos 攻击等。

    负载均衡的意义

    大型网站都要面对庞大的用户量,高并发,海量数据等挑战。为了提升系统整体的性能,可以采用垂直扩展和水平扩展两种方式。

    垂直扩展:在网站发展早期,可以从单机的角度通过增加硬件处理能力,比如 CPU 处理能力,内存容量,磁盘等方面,实现服务器处理能力的提升。但是,单机是有性能瓶颈的,一旦触及瓶颈,再想提升,付出的成本和代价会极高。这显然不能满足大型分布式系统(网站)所有应对的大流量,高并发,海量数据等挑战。

    水平扩展:通过集群来分担大型网站的流量。集群中的应用服务器(节点)通常被设计成无状态,用户可以请求任何一个节点,这些节点共同分担访问压力。水平扩展有两个要点:

    • 应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理,并返回相应数据。
    • 负载均衡:将用户访问请求,通过某种算法,分发到集群中的节点。

    负载均衡相关算法

    1、轮询法

    • 将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
        private static Integer pos = 0;
        public String getHostByPoll(){
            List<String> hosts = Host.getHostList();
            String host;
            synchronized (pos){
                if(pos >= hosts.size()){
                    pos = 0;
                }
                host = hosts.get(pos++);
            }
            return host;
        }
    

    2、随机法

    • 通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。
    • 由概率统计理论可以得知,随着客户端调用服务端的次数增多,每台主机处理请求数越接近平均值。
        public String getHostByRandom(){
            List<String> hosts = Host.getHostList();
            int randomNum = new Random().nextInt(hosts.size());
            System.out.println(randomNum);
            return hosts.get(randomNum);
        }
    

    3、加权轮询法

    • 不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。
    • 给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
        private static Integer pos = 0;
    
        public String getHostByPollWeight(){
            Map<String,Integer> hosts = Host.getHostMap2();
            String host = null;
            synchronized (pos){
                for(Map.Entry<String,Integer> entry : hosts.entrySet()){
                    if(pos >= Host.totalWeight){
                        pos = 0;
                    }
                    if(pos < entry.getValue()){
                        host = entry.getKey();
                        pos++;
                        break;
                    }
                }
            }
            return host;
        }
    

    4、加权随机法

    • 不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。
    • 给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载。
    • 与加权轮询法不同的是,它是按照权重随机请求后端服务器。
        public String getHostByRandomWeight(){
            Map<String ,Integer> hosts = Host.getHostMap();
            int randomNum = new Random().nextInt(Host.totalWeight);
            for(Map.Entry<String,Integer> entry : hosts.entrySet()){
                if(randomNum < entry.getValue()){
                    return entry.getKey();
                }else{
                    randomNum -= entry.getValue();
                }
            }
            return null;
        }
    

    5、源地址哈希法

    • 源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,
    • 用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。
    • 采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
        public String getHostByIpHash(int ipHash) {
            List<String> hosts = Host.getHostList();
            int pos = ipHash % hosts.size();
            return hosts.get(pos);
        }
    
        //模拟随机客户端的hash值
        public int getClientHash() {
            return new Random().nextInt(3);
        }
    

    6、最小连接数法

    • 由于后端服务器的配置不尽相同,对于请求的处理有快有慢。
    • 它正是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最小的一台服务器来处理当前请求,尽可能地提高后端服务器的利用效率,将负载合理地分流到每一台机器。
        public Server getServerByLeastConnect(){
            List<Server> serverList = Host.getServerList();
            if(serverList == null || serverList.size() == 0){
                return null;
            }
            Server minConnectServer = serverList.get(0);
            for(int i = 1; i < serverList.size();i++){
                Server server = serverList.get(i);
                if(minConnectServer.getConnect() > server.getConnect()){
                    minConnectServer = server;
                }
            }
            minConnectServer.addConnect();
            return minConnectServer;
        }
    

    其中Host源码如下

    public class Host {
    
    
        private static List<String> hostList;
        private static Map<String,Integer> hostMap;
        private static Map<String,Integer> hostMap2;
        public static int totalWeight;
    
        static {
            //初始化主机ip地址
            initHostList();
            initHostMap();
            initHostMap2();
        }
    
        public static void initHostList(){
            hostList = new ArrayList<>();
            hostList.add("192.168.0.1");
            hostList.add("192.168.0.2");
            hostList.add("192.168.0.3");
        }
    
        public static void initHostMap(){
            hostMap = new HashMap<>();
            //这里为了方便用A、B、C代表三个主机ip
            hostMap.put("A",5);
            hostMap.put("B",2);
            hostMap.put("C",3);
    
            Map<String ,Integer> hosts = Host.getHostMap();
            for(int v : hosts.values()){
                totalWeight += v;
            }
        }
    
        public static void initHostMap2(){
            hostMap2 = new HashMap<>();
            int weight = 0;
            for(Map.Entry<String,Integer> entry : hostMap.entrySet()){
                weight += entry.getValue();
                hostMap2.put(entry.getKey(),weight);
            }
    
        }
    
        public static List<String> getHostList(){
            return hostList;
        }
    
        public static Map<String,Integer> getHostMap(){
            return hostMap;
        }
    
        public static Map<String,Integer> getHostMap2(){
            return hostMap2;
        }
    }
    

    文章所有源码地址都放到了Github上了,有兴趣的小伙伴可以下载下来看看哦。
    GitHub源码地址:git@github.com:HelloWorld19930603/alien_cat.git
    该算法源码对应目录:alien_cat/algorithm/src/main/java/com/aliencat/algorithm/lb

    cs