当前位置: 首页 > 科技观察

各种负载均衡算法及其Java代码实现

时间:2023-03-21 11:11:29 科技观察

首先介绍一下什么是负载均衡(来自维基百科)负载均衡是建立在现有的网络结构之上,它提供了一种廉价、有效、透明的扩展方法网络设备和服务器带宽,增加吞吐量,增强网络数据处理能力,提高网络灵活性和可用性。负载均衡,英文名称LoadBalance,意思是分布到多个运行单元执行,如Web服务器、FTP服务器、企业关键应用服务器等关键任务服务器,共同完成工作任务.本文介绍了“以对称结构将外部请求均匀分布到某台服务器”的各种算法,并用Java代码演示了各个算法的具体实现。OK,进入正题,接下来进入正题首先写一个模拟Ip列表的类:importjava.util.HashMap;/***@authorashang.peng@aliyun.com*@dateFebruary07,2017*/publicclassIpMap{//待路由的Ip列表,Key代表Ip,Value代表Ip的权重publicstaticHashMapserverWeightMap=newHashMap();static{serverWeightMap.put("192.168.1.100",1);服务器权重图。放("192.168.1.101",1);//权重为4serverWeightMap.put("192.168.1.102",4);serverWeightMap.put("192.168.1.103",1);serverWeightMap.put("192.168.1.104",1);//权重为3serverWeightMap.put("192.168.1.105",3);serverWeightMap.put("192.168.1.106",1);//权重为2serverWeightMap.put("192.168.1.107",2);serverWeightMap.put("192.168.1.108",1);服务器WeightMap.put("192.168.1.109",1);serverWeightMap.put("192.168.1.110",1);}}RoundRobin方法roundrobin调度算法的原理是每次将来自用户的请求依次分配给内部服务器,从1开始,直到N(内部服务器的数量),然后重新开始循环算法有优势由于其简单性,不需要记录当前所有连接的状态,因此是一种无状态调度。代码实现大致如下:importjava.util.ArrayList;importjava.util.HashMap;importjava.util.Map;importjava.util.Set;/***@authorashang.peng@aliyun.com*@date2017年2月7日*/classRoundRobin{privatestaticIntegerpos=0;publicstaticStringgetServer(){//重建Map,避免服务器下线导致的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址列表SetkeySet=serverMap.keySet();ArrayListkeyList=newArrayList();键列表。添加所有(键集);字符串服务器=空;同步(pos){如果(pos>keySet.size())pos=0;server=keyList.get(pos);位置++;}返回服务器;}}由于serverWeightMap中的地址列表是动态的,机器随时可能上线、下线或崩溃。因此,为了避免可能出现的并发问题,在方法内部新建了一个局部变量serverMap。现在将serverMap的内容复制到线程本地,避免被多线程修改。这可能会引入新的问题。复制后,serverWeightMap的修改无法反映到serverMap。也就是说,在这一轮的服务器选择过程中,负载均衡算法将无法知道新的服务器或下线的服务器。加一个新的也没关系。如果服务器掉线或崩溃,您可能会访问一个不存在的地址。因此,服务调用者需要有相应的容错处理,如重新发起服务器选择和调用。对于当前轮询的位置变量pos,为了保证服务器选择的顺序,运行时需要加锁,这样一次只能有一个线程修改pos的值,否则并发修改pos变量时,不能保证服务器选择的顺序,甚至可能导致keyList数组越界。轮询方法的优点是它试图实现请求传输的绝对平衡。轮询方式的缺点是为了实现请求传输的绝对平衡,必须付出相当大的代价,因为为了保证pos变量修改的互斥性,需要引入重量级的悲观锁synchronized,这将导致轮查询代码的并发吞吐量显着下降。随机(Random)方式是利用系统的随机算法,根据后端服务器的listsize值,随机选择其中一台服务器进行访问。由概率统计理论可知,随着客户端调用服务端的次数增加,其实际效果越来越接近后端各服务端的调用平均分布,这就是结果投票。random方法的代码实现大致如下:importjava.util.ArrayList;importjava.util.HashMap;importjava.util.Map;importjava.util.Set;/***@authorashang.peng@aliyun.com*@dateFebruary07,2017*/classRandom{publicstaticStringgetServer(){//重新构建一个Map,避免服务器下线导致的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址列表SetkeySet=serverMap.keySet();ArrayListkeyList=newArrayList();keyList.addAll(keySet);java.util.Randomrandom=newjava.util.Random();intrandomPos=random.nextInt(keyList.size());返回keyList.get(randomPos);}}整体代码思路与轮询方式一致,先RebuildserverMap,再获取服务器列表。选择服务器时,使用Random的nextInt方法在0~keyList.size()范围内取一个随机值,从而从服务器列表中随机获取一个服务器地址并返回。根据概率统计理论,吞吐量越大,随机算法的效果越接近轮询算法的效果。源地址哈希(Hash)方法源地址哈希的思想是根据客户端的IP地址通过哈希函数计算得到一个值,用这个值对服务器列表的大小进行取模运算,得到的结果是客户端要访问的服务器的序号。源地址散列法用于负载均衡。对于IP地址相同的客户端,在后端服务器列表不变的情况下,每次都会映射到同一台后端服务器进行访问。源地址哈希算法的代码实现大致如下:importjava.util.ArrayList;importjava.util.HashMap;importjava.util.Map;importjava.util.Set;/***@authorashang。peng@aliyun.com*@dateFebruary07,2017*/classHash{publicstaticStringgetServer(){//重新构建Map,避免服务器下线导致的并发问题MapserverMap=newHashMap<字符串、整数>();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址列表SetkeySet=serverMap.keySet();ArrayListkeyList=newArrayList();keyList.addAll(keySet);//String可以通过web应用中HttpServlet的getRemoteIp方法获取remoteIp="127.0.0.1";inthashCode=remoteIp.hashCode();intserverListSize=keyList.size();intserverPos=hashCode%serverListSize;返回keyList.get(serverPos);}}前两部分与轮询方法和随机方法相同。区别在于路由部分。通过客户端的ip,即remoteIp,获取其Hash值,对服务器列表的大小进行取模,得到的结果就是所选服务器在服务器列表中的索引值。源地址散列法的优点是保证相同的客户端IP地址会被散列到同一个后端服务器,直到后端服务器列表发生变化。根据这个特点,可以在服务消费者和服务提供者之间建立一个有状态的会话会话。源地址哈希算法的缺点是,除非集群中的服务器非常稳定,否则基本不会上线或下线。否则,一旦服务器上线或下线,源地址哈希算法路由到的服务器被路由到的服务器的概率很低。如果是session,则获取不到session。如果是缓存,可能会造成“雪崩”。如果这个解释不适合理解,可以看我上一篇关于MemCache的超详细解释,一致性Hash算法部分。不同weightedroundrobin方式的后端服务器可能机器配置不同,当前系统负载不同,因此其抗压能力也不同。给配置高、负载低的机器分配更高的权重,让它们处理更多的请求;给配置低、负载高的机器分配较低的权重,以降低其系统负载,加权轮询效果很好妥善处理这个问题,将请求按权重顺序分配给后端。加权循环法的代码实现大致如下:importjava.util.*;/***@authorashang.peng@aliyun.com*@dateFebruary07,2017*/classWeightRoundRobin{privatestaticInteger位置;publicstaticStringgetServer(){//重建Map,避免服务器下线导致的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址列表SetkeySet=serverMap.keySet();Iteratoriterator=keySet.iterator();ListserverList=newArrayList();while(iterator.hasNext()){Stringserver=iterator.next();intweight=serverMap.get(服务器);for(inti=0;ikeySet.size())pos=0;服务器=serverList.get(pos);位置++;}返回服务器;}}与轮询方式类似,只是在获取服务器地址之前增加了一段权重计算代码,根据权重将地址重复添加到服务器地址列表中,权重越大,越多请求服务器在每一轮中获得。加权随机法与加权循环法相同。加权随机法也是根据后端机器的配置和系统的负载分配不同的权重。.不同的是,它是按权重随机请求后端服务器,而不是按顺序。importjava.util.*;/***@authorashang.peng@aliyun.com*@dateFebruary07,2017*/classWeightRandom{publicstaticStringgetServer(){//重建一个Map,避免serverup和down行引起的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址列表SetkeySet=serverMap.keySet();Iteratoriterator=keySet.iterator();ListserverList=newArrayList();while(iterator.hasNext()){Stringserver=iterator.next();intweight=serverMap.get(server);for(inti=0;i