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

几种简单的负载均衡算法及其Java代码实现_0

时间:2023-03-15 14:44:47 科技观察

1.什么是负载均衡?无需其他服务器的协助,即可独立对外提供服务。通过一定的负载分担技术,将外界发来的请求以对称结构均匀分布到某台服务器上,接收请求的服务器独立响应客户端的请求。负载均衡可以将客户的请求均匀分布到服务器阵列上,从而提供对重要数据的快速访问,解决大量并发访问的服务问题。这种集群技术可以用最少的投资获得接近大型主机的性能。负载均衡分为软件负载均衡和硬件负载均衡。前者以阿里张文松博士开发的LVS为代表,后者则是F5等平衡服务器。当然,这只是提一下,不是重点。本文介绍了“以对称结构将外部请求均匀分布到某台服务器”的各种算法,并用Java代码演示了各个算法的具体实现。首先写一个类来模拟Ip列表:publicclassIpMap{//要路由的Ip列表,Key代表Ip,Value代表Ip的权重publicstaticHashMapserverWeightMap=newHashMap();static{serverWeightMap.put("192.168.1.100",1);serverWeightMap.put("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);//weightis2",1);}}02.轮询(RoundRobin)方法轮询方法即RoundRobin方法,其代码实现大致如下:publicclassRoundRobin{privatestaticIntegerpos=0;publicstaticStringgetServer(){//重建一个Map,避免服务器下线Ma导致的并发问题pserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取Ip地址ListSetkeySet=serverMap.keySet();ArrayListkeyList=newArrayList();keyList.addAll(keySet);Stringserver=null;synchronized(pos){if(pos>keySet.size())pos=0;server=keyList.get(pos);pos++;}returnserver;}}由于serverWeightMap中的地址列表是动态的,随时可能有机器在线、离线或宕机,所以为了避免可能出现的并发问题,在方法内部新建了一个局部变量serverMap,serverMap中的内容为现在复制到线程本地,避免被多线程修改,可能会引入新的问题。复制后,serverWeightMap的修改无法反映到serverMap。不会被知道。加一个新的也没关系。如果服务器掉线或崩溃,您可能会访问一个不存在的地址。因此,服务调用者需要有相应的容错处理,如重新发起服务器选择和调用。对于当前轮询的位置变量pos,为了保证服务器选择的顺序,运行时需要加锁,这样一次只能有一个线程修改pos的值,否则并发修改pos变量时,不能保证服务器选择的顺序,甚至可能导致keyList数组越界。轮询方法的优点是它试图实现请求传输的绝对平衡。轮询方式的缺点是为了实现请求传输的绝对平衡,必须付出相当大的代价,因为为了保证pos变量修改的互斥性,需要引入重量级的悲观锁synchronized,这将导致轮查询代码的并发吞吐量显着下降。03.随机(Random)方式利用系统随机函数,根据后端服务器列表的大小值,随机选择其中一个后端服务器列表进行访问。从概率统计理论可以知道,随着调用次数的增加,实际效果越来越接近平均分配流量到各个后端服务器,即轮询的效果。random方法的代码实现大致如下:publicclassRandom{publicstaticStringgetServer(){//重新构建一个Map,避免服务器上线下线带来的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址ListSetkeySet=serverMap.keySet();ArrayListkeyList=newArrayList();keyList.addAll(keySet);java.util.Randomrandom=newjava。util.Random();intrandomPos=random.nextInt(keyList.size());returnkeyList.get(randomPos);}}整体代码思路与轮询方式一致,先重建serverMap,再获取服务器列表.选择服务器时,使用Random的nextInt方法在0~keyList.size()范围内取一个随机值,从而从服务器列表中随机获取一个服务器地址并返回。根据概率统计理论,吞吐量越大,随机算法的效果越接近轮询算法的效果。04.源地址哈希(Hash)法源地址哈希的思想是获取客户端访问的IP地址值,通过哈希函数计算出一个值,用这个值对大小进行取模运算要获取的服务器列表结果是要访问的服务器的序号。源地址哈希算法的代码实现大致如下:publicclassHash{publicstaticStringgetServer(){//重新构建一个Map,避免服务器下线和下线导致的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取Ip地址ListSetkeySet=serverMap.keySet();ArrayListkeyList=newArrayList();keyList.addAll(keySet);//在网络应用中StringremoteIp="127.0.0.1";inthashCode=remoteIp.hashCode();intserverListSize=keyList.size();intserverPos=hashCode%serverListSize;returnkeyList.get(serverPos);}}前两部分可以通过HttpServlet的getRemoteIp方法获取,轮询方法和随机方法是一样的。区别在于路由选择部分。通过客户端的ip,即remoteIp,获取其Hash值,对服务器列表的大小进行取模,得到的结果就是所选服务器在服务器列表中的索引值。源地址散列法的优点是保证相同的客户端IP地址会被散列到同一个后端服务器,直到后端服务器列表发生变化。根据这个特点,可以在服务消费者和服务提供者之间建立一个有状态的会话会话。源地址哈希算法的缺点是,除非集群中的服务器非常稳定,否则基本不会上线或下线。否则,一旦服务器上线或下线,源地址哈希算法路由到的服务器被路由到的服务器的概率很低。如果是session,则获取不到session。如果是缓存,可能会造成“雪崩”。05.加权轮循(WeightRoundRobin)不同的服务器可能机器配置和当前系统负载不同,因此其抗压能力也不同。为高配置低负载的机器配置更高的权重,让它处理更多的请求,为低配置高负载的机器分配更低的权重,以降低其系统负载。weightedround-robin方法可以很好的处理这个问题,根据权重给后端分配请求的顺序。weightedround-robin方法的代码实现大致如下:publicclassWeightRoundRobin{privatestaticIntegerpos;publicstaticStringgetServer(){//重新构建一个Map,避免服务器上下线带来的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取Ip地址ListSetkeySet=serverMap.keySet();Iteratoriterator=keySet.iterator();ListserverList=newArrayList();while(iterator.hasNext()){Stringserver=iterator.next();intweight=serverMap.get(server);for(inti=0;iserverList.size())pos=0;server=serverList.get(pos);pos++;}returnserver;}}与轮询方式类似,只是增加了一块根据权重向服务器地址列表中重复添加地址的权重计算代码。权重越大,服务器每轮收到的请求越多。06.加权随机(WeightRandom)法类似于加权循环法。加权随机法也是根据后端服务器的不同配置和负载情况配置不同的权重。不同之处在于它按权重而不是顺序随机选择服务器。加权随机方法的代码实现如下:publicclassWeightRandom{publicstaticStringgetServer(){//重新构建一个Map,避免服务器上线下线带来的并发问题MapserverMap=newHashMap();serverMap.putAll(IpMap.serverWeightMap);//获取IP地址ListSetkeySet=serverMap.keySet();Iteratoriterator=keySet.iterator();ListserverList=newArrayList();while(迭代器.hasNext()){Stringserver=iterator.next();intweight=serverMap.get(server);for(inti=0;i