项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步

负载均衡算法在实际中使用及其广泛,比如某个大型网站或者某个大型服务,服务器的数量可能成百上千台,这个时候是一定会有负载均衡算法的。现在总结一下常见的几种负载均衡算法。

1.轮询法(RoundRobin)

轮询法是一种比较简单的算法,也比较好理解。假设后台有N台服务器,那么来请求以后,按照请求到达的先后顺序,将流量分配到服务器上,这样后端服务器上的流量都是相同的。这种方式的好处是不用关注服务器本身的负载,链接数等信息。但是坏处也比较明显,如果每台服务器的处理能力不一样,那比较强的服务器就无法发挥优势。Nginx中默认的就是轮询法。

import java.util.Arrays;
import java.util.List;/*** Created by WangLei on 18-6-21.*/
public class RoundRobin {private static List<String> servers = Arrays.asList("192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4");private static Integer pos = 0;public static String getServer() {String server = null;synchronized (pos) {if(pos == servers.size()) {pos = 0;}server = servers.get(pos);pos++;}return server;}public static void main(String[] args) {for(int i=0; i<10; i++) {System.out.println(RoundRobin.getServer());}}
}

运行结果:

192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.1
192.168.0.2

代码里面需要注意的是对于pos参数的处理,因为实际中并发的场景很常见,所以用synchronized将pos包起来,这样保证pos每次只被一个线程修改。

2.随机法

随机的方式也比较容易理解。将流量随机分配到后端某一台服务器上。根据简单的统计理论可以得知,随着流量越来越大,随机分配的实际效果越来越接近平均分配,最终的实际效果跟轮询一致。

import java.util.Arrays;
import java.util.List;
import java.util.Random;/*** Created by WangLei on 18-6-21.*/
public class RandomMethod {private static List<String> servers = Arrays.asList("192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4");private static int pos = 0;public static String getServer() {String server = null;Random random = new Random();int randomPos = random.nextInt(servers.size());server = servers.get(randomPos);return server;}public static void main(String[] args) {for(int i=0; i<10; i++) {System.out.println(RandomMethod.getServer());}}}
192.168.0.4
192.168.0.4
192.168.0.1
192.168.0.4
192.168.0.3
192.168.0.1
192.168.0.4
192.168.0.3
192.168.0.1
192.168.0.3

根据代码可以看出,在并发的情况下,随机方式不需要像轮询一样加锁,并发能力比轮询要强。

3.主备算法

这个算法核心的思想是将请求尽量的放到某个固定机器的服务上,而其他机器的服务则用来做备份,如果出现问题就切换到另外的某台机器的服务上。这么做的好处之一就是每个流量分配到哪个服务器上是固定的,在某些场合会比较方便。

比如我们下面实现一个简单的主备,根据客户端的ip将流量分配到固定的机器上。

import java.util.Arrays;
import java.util.List;/*** Created by WangLei on 18-6-21.*/
public class HashMethod {private static List<String> servers = Arrays.asList("192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4");private static int pos = 0;public static String getServer(String ip) {String server = null;int hashCode = ip.hashCode();pos = hashCode % servers.size();server = servers.get(pos);return server;}public static void main(String[] args) {String ip = "192.168.255.254";System.out.println(HashMethod.getServer(ip));}
}
192.168.0.3

4.一致性Hash

一致性Hash算法通过一个叫做一致性Hash环的数据结构实现Key到后端服务器的Hash映射。如果是缓存服务,实现的则是Key到缓存服务器的Hash映射。从网上找了一张示意图。

算法的具体逻辑如下:将[0,2^32)所有的整数投射到一个圆上,然后再将你的机器的唯一编码(比如:IP)通过hash运算得到的整数也投射到这个圆上(Node-A、Node-B)。如果一个请求来了,就将这个请求的唯一编码(比如:用户id)通过hash算法运算得到的整数也投射到这个圆上(request-1、request-2),通过顺时针方向,找到第一个对应的机器。

一致性Hash需要解决的是以下两个问题:
1、散列的不变性:就是同一个请求(比如:同一个用户id)尽量的落入到一台机器,不要因为时间等其他原因,落入到不同的机器上了;
2、异常以后的分散性:当某些机器坏掉(或者增加机器),原来落到同一台机器的请求(比如:用户id为1,101,201),尽量分散到其他机器,不要都落入其他某一台机器。这样对于系统的冲击和影响最小。
一致Hash算法用的最多的场景,就是分配cache服务。将某一个用户的数据缓存在固定的某台服务器上,那么我们基本上就不用多台机器都缓存同样的数据,这样对我们提高缓存利用率有极大的帮助。
(此部分来自https://blog.csdn.net/zgwangbo/article/details/51533657)

先看一个一致性Hash的简单实现版本。

import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;/*** Created by WangLei on 18-6-21.*/
public class ConsisentHashNoVirtualNode {private static String[] serversarray = {"192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111","192.168.0.3:111", "192.168.0.4:111"};private static List<String> servers = Arrays.asList(serversarray);private static SortedMap<Integer, String> sortedMap = new TreeMap<>();static {for(int i=0; i<servers.size(); i++) {int hash = hashCode(servers.get(i));System.out.println(servers.get(i) + " join collections, hashcode is: " + hash);sortedMap.put(hash, servers.get(i));}System.out.println();}// 重写hashcode方法,使用FNV1_32_HASH算法,让节点在hash环上分布更均匀。private static int hashCode(String ip) {final int p = 16777619;int hash = (int)2166136261L;for(int i=0; i<ip.length(); i++) {hash = (hash ^ ip.charAt(i)) * p;}hash += hash << 13;hash ^= hash >> 7;hash += hash << 3;hash ^= hash >> 17;hash += hash << 5;if(hash < 0) {hash = Math.abs(hash);}return hash;}private static String getServer(String client) {int hash = hashCode(client);// 得到大于该Hash值的所有Map。注意有可能subMap一个值没有,此时默认分给第一个server。SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);if(subMap != null && subMap.size() > 0) {int i = subMap.firstKey();return subMap.get(i);} else {return sortedMap.get(hashCode(servers.get(0)));}}public static void main(String[] args) {String[] clients =  {"127.0.0.1:22", "221.226.0.1:80", "10.211.0.1:8080"};//String[] clients =  {"127.0.0.1:22", "221.226.0.1:80"};for(int i=0; i<clients.length; i++) {System.out.println(clients[i] + " hashcode is: " + hashCode(clients[i]) + ", the router node is: " + getServer(clients[i]));}}
}

运行结果:

192.168.0.0:111 join collections, hashcode is: 575774686
192.168.0.1:111 join collections, hashcode is: 8518713
192.168.0.2:111 join collections, hashcode is: 1361847097
192.168.0.3:111 join collections, hashcode is: 1171828661
192.168.0.4:111 join collections, hashcode is: 1764547046127.0.0.1:22 hashcode is: 1739501660, the router node is: 192.168.0.4:111
221.226.0.1:80 hashcode is: 99109700, the router node is: 192.168.0.0:111
10.211.0.1:8080 hashcode is: 1976495495, the router node is: 192.168.0.0:111

上面的一致性Hash算法实现,可以在很大程度上解决很多分布式环境下不好的路由算法导致系统伸缩性差的问题,但是会带来另外一个问题:负载不均。

比如说有Hash环上有A、B、C三个服务器节点,分别有100个请求会被路由到相应服务器上。现在在A与B之间增加了一个节点D,这导致了原来会路由到B上的部分节点被路由到了D上,这样A、C上被路由到的请求明显多于B、D上的,原来三个服务器节点上均衡的负载被打破了。某种程度上来说,这失去了负载均衡的意义,因为负载均衡的目的本身就是为了使得目标服务器均分所有的请求。

解决这个问题的办法是引入虚拟节点,其工作原理是:将一个物理节点拆分为多个虚拟节点,并且同一个物理节点的虚拟节点尽量均匀分布在Hash环上。采取这样的方式,就可以有效地解决增加或减少节点时候的负载不均衡的问题。

import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;/*** Created by WangLei on 18-6-21.*/
public class ConsisenthashWithVituralNode {private static String[] serversarray = {"192.168.0.1","192.168.0.2","192.168.0.3","192.168.0.4"};private static List<String> realservers = new LinkedList<>();private static final int VIRTUAL_NUM = 5;private static SortedMap<Integer, String> sortedMap = new TreeMap<>();static {for(int i=0; i<serversarray.length; i++) {realservers.add(serversarray[i]);}//双层循环,每个实际节点都添加对应的虚拟节点for(String ip : realservers) {for(int i = 0; i < VIRTUAL_NUM; i++) {String virtualNode = ip + "#" + String.valueOf(i);int hash = hashCode(virtualNode);System.out.println("virtual node of " + virtualNode + " add, hash is: " + hash);sortedMap.put(hash, virtualNode);}}System.out.println();}private static int hashCode(String input) {final int p = 16777619;int hash = (int)2166136261L;for(int i=0; i<input.length(); i++) {hash = (hash ^ input.charAt(i)) * p;}hash += hash << 13;hash ^= hash >> 7;hash += hash << 3;hash ^= hash >> 17;hash += hash << 5;if(hash < 0) {hash = Math.abs(hash);}return hash;}private static String getServer(String client) {String virtualNode = null;int hash = hashCode(client);SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);if(subMap != null && subMap.size() > 0) {int i = subMap.firstKey();virtualNode = subMap.get(i);} else {virtualNode = sortedMap.get(serversarray[0]);}return virtualNode.substring(0, virtualNode.indexOf("#"));}public static void main(String[] args) {String[] clients =  {"127.0.0.1:22", "110.226.0.1:80", "255.21.0.1:8080"};for(int i=0; i<clients.length; i++) {System.out.println(clients[i] + " hashcode is: " + hashCode(clients[i]) + " the router node is: " + getServer(clients[i]));}}
}

运行结果:

virtual node of 192.168.0.1#0 add, hash is: 1267794962
virtual node of 192.168.0.1#1 add, hash is: 1405473402
virtual node of 192.168.0.1#2 add, hash is: 520282580
virtual node of 192.168.0.1#3 add, hash is: 902681916
virtual node of 192.168.0.1#4 add, hash is: 705135863
virtual node of 192.168.0.2#0 add, hash is: 938864723
virtual node of 192.168.0.2#1 add, hash is: 697737174
virtual node of 192.168.0.2#2 add, hash is: 758522939
virtual node of 192.168.0.2#3 add, hash is: 1305037326
virtual node of 192.168.0.2#4 add, hash is: 1002536378
virtual node of 192.168.0.3#0 add, hash is: 392944983
virtual node of 192.168.0.3#1 add, hash is: 2046386183
virtual node of 192.168.0.3#2 add, hash is: 1604877649
virtual node of 192.168.0.3#3 add, hash is: 95135893
virtual node of 192.168.0.3#4 add, hash is: 2030051947
virtual node of 192.168.0.4#0 add, hash is: 1614569944
virtual node of 192.168.0.4#1 add, hash is: 744068005
virtual node of 192.168.0.4#2 add, hash is: 1628537157
virtual node of 192.168.0.4#3 add, hash is: 537291676
virtual node of 192.168.0.4#4 add, hash is: 1173079769127.0.0.1:22 hashcode is: 1739501660 the router node is: 192.168.0.3
110.226.0.1:80 hashcode is: 1320900860 the router node is: 192.168.0.1
255.21.0.1:8080 hashcode is: 1187650066 the router node is: 192.168.0.1

负载均衡算法详解与实践相关推荐

  1. Dubbo内置4种负载均衡算法(详解)

    1.1 什么是负载均衡 在实际开发中,一个服务基本都是集群模式的,也就是多个功能相同的项目在运行,这样才能承受更高的并发,这时一个请求到这个服务,就需要确定访问哪一个服务器 Dubbo框架内部支持负载 ...

  2. 思科ccna认证VRRP负载均衡技术详解

    在VRRP标准协议模式中,只有Master路由器可以转发报文,Backup路由器处于监听状态,无法转发报文.虽然创建多个备份组可以实现多个路由器之间的负载分担,但是局域网内的主机需要设置不同的网关,增 ...

  3. NEAT(NeuroEvolution of Augmenting Topologies)算法详解与实践(基于NEAT-Python)

    NEAT(NeuroEvolution of Augmenting Topologies)算法详解与实践(基于NEAT-Python) NEAT算法详解 NEAT算法概述 NEAT编码方案 结构突变 ...

  4. id3算法c语言实现,从ID3到C5.0的故事:算法详解及实践应用

    原标题:从ID3到C5.0的故事:算法详解及实践应用 在前面,我们分别概述性地介绍了决策树的基本知识: 1.算法概述 ID3(Iterative Dichotomiser3)算法可以说决策树算法中最著 ...

  5. Nacos系列--权重(负载均衡)--作用/详解

    原文网址:Nacos系列--权重(负载均衡)--作用/详解_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Nacos的权重的用法,它是一种负载均衡的方法. 权重的含义 Nacos控制台可以设置 ...

  6. 分布式架构系列: 负载均衡技术详解 | 技术头条

    戳蓝字"CSDN云计算"关注我们哦! 技术头条:干货.简洁.多维全面.更多云计算精华知识尽在眼前,get要点.solve难题,统统不在话下! 作者:ITFLY8 转自:架构师技术联 ...

  7. nginx的负载均衡模块详解

    反向代理与负载均衡的概念: Nginx仅仅是作为nginx proxy反向代理使用,nginx其实是反向代理,只不过是有负载均衡的功能! 安装Nginx负载均衡 官网Nginx模块讲解 模块详解 up ...

  8. nginx反向代理及负载均衡使用详解

    nginx反向代理及负载均衡使用说明 文章目录 nginx反向代理及负载均衡使用说明 集群介绍 负载均衡架构图 为什么要使用集群 集群种类 负载均衡概念说明 压力测试 反响代理概念说明 反向代理图解 ...

  9. LVS负载均衡--知识详解

    一. 集群的概念 服务器集群简称集群是一种服务器系统,它通过一组松散集成的服务器软件和/或硬件连接起来高度紧密地协作完成计算工作.在某种意义上,他们可以被看作是一台服务器. 集群系统中的单个服务器通常 ...

  10. 均衡原理_干货什么是负载均衡?负载均衡原理详解

    负载均衡是高可用网络基础架构的一个关键组成部分,有了负载均衡,我们通常可以将我们的应用服务器部署多台,然后通过负载均衡将用户的请求分发到不同的服务器用来提高网站.应用.数据库或其他服务的性能以及可靠性 ...

最新文章

  1. R绘制带显著性标记的热图
  2. Android 记住密码和自动登录界面的实现(SharedPreferences 的用法)(转载)
  3. list替换某一项需要遍历比对再set到对应的position
  4. 微信小程序 倒计时实现
  5. 预训练模型transformers综合总结(一)
  6. Spark-1.6.0之Application运行信息记录器JobProgressListener
  7. 《系统集成项目管理工程师》必背100个知识点-64采购文件
  8. 叹息“博客园”的凋零
  9. 控制項學習三(從繼承開始)
  10. 魔幻艰难的2020上半年!
  11. hibernate查询缓存_在Hibernate中启用实体和查询缓存
  12. mysql基础和高级整理_mysql基础整理01
  13. C#: Writing a CookieContainer to Disk and Loading Back In For Use
  14. 基金指数温度怎么算_壁挂炉采暖费怎么算?从两千到八百,内行人教你别再花冤枉钱...
  15. 计算最大子段(分治法)
  16. Educational Codeforces Round 54 (Rated for Div. 2): D. Edge Deletion(最短路树)
  17. Windows窗口固定工具
  18. 芯片和计算机专业的关系,cpu是芯片吗?芯片和cpu是什么关系?
  19. cimiss java,cimis
  20. 1976年图灵奖--米凯尔·拉宾和达纳·斯科特简介

热门文章

  1. Ubuntu下VirtualBox的vdi文件克隆
  2. quartz job基本运用
  3. web controls归档
  4. hashmap扩容机制 jdk1.7
  5. 在IE情况下兼容 axios 的问题
  6. 为何不让AI做更正确的抉择?人类要逐渐交出控制权
  7. 訪问可能没有定义的data (通过static类型flash.net:FileReference引用)
  8. Matlab学习(可以用MATLAB作曲)
  9. u盘安装ubuntu server 14.04 以及No CD-ROM drive was detected 错误
  10. 【设计模式】享元模式(Flyweight)