7.负载均衡算法

本章内容:

  1.轮询

  2.最少链接

  3.随机算法

  4.源地址哈希法

  5.加权轮询、加权最少连接、加权随机


 【准备:客户端请求IP】

 1 public class IpMap
 2 {
 3     // 待路由的Ip列表,Key代表Ip,Value代表该Ip的权重
 4     public static HashMap<String, Integer> serverWeightMap = new HashMap<String, Integer>();
 5     
 6     static
 7     {
 8         serverWeightMap.put("192.168.1.100", 1);
 9         serverWeightMap.put("192.168.1.101", 1);
10         // 权重为4
11         serverWeightMap.put("192.168.1.102", 4);
12         serverWeightMap.put("192.168.1.103", 1);
13         serverWeightMap.put("192.168.1.104", 1);
14         // 权重为3
15         serverWeightMap.put("192.168.1.105", 3);
16         serverWeightMap.put("192.168.1.106", 1);
17         // 权重为2
18         serverWeightMap.put("192.168.1.107", 2);
19         serverWeightMap.put("192.168.1.108", 1);
20         serverWeightMap.put("192.168.1.109", 1);
21         serverWeightMap.put("192.168.1.110", 1);
22     }
23 }

一、轮询(Round Robin)

  轮询算法将每个请求轮流发送到每个服务器上。(一碗水端平)

  

(接收到六个请求,平均轮次分发给两个服务器进行处理。)

 1 public class RoundRobin
 2 {
 3     private static Integer pos = 0;
 4     
 5     public static String getServer()
 6     {
 7         // 重建一个Map,避免服务器的上下线导致的并发问题
 8         Map<String, Integer> serverMap = new HashMap<String, Integer>();
 9         serverMap.putAll(IpMap.serverWeightMap);
10         
11         // 取得Ip地址List
12         Set<String> keySet = serverMap.keySet();
13         ArrayList<String> keyList = new ArrayList<String>();
14         keyList.addAll(keySet);
15         
16         String server = null;
17         synchronized (pos)
18         {
19             if (pos > keySet.size())
20                 pos = 0;
21             server = keyList.get(pos);
22             pos ++;
23         }
24         
25         return server;
26     }
27 }

  由于serverWeightMap中的地址列表是动态的,随时可能有机器上线、下线或者宕机,因此为了避免可能出现的并发问题,方法内部要新建局部变量serverMap,现将serverMap中的内容复制到线程本地,以避免被多个线程修改。这样可能会引入新的问题,复制以后serverWeightMap的修改无法反映给serverMap,也就是说这一轮选择服务器的过程中,新增服务器或者下线服务器,负载均衡算法将无法获知。新增无所谓,如果有服务器下线或者宕机,那么可能会访问到不存在的地址。因此,服务调用端需要有相应的容错处理,比如重新发起一次server选择并调用

  对于当前轮询的位置变量pos,为了保证服务器选择的顺序性,需要在操作时对其加锁,使得同一时刻只能有一个线程可以修改pos的值,否则当pos变量被并发修改,则无法保证服务器选择的顺序性,甚至有可能导致keyList数组越界。

  优点:试图做到请求转移的绝对平衡。

  缺点:为了保证请求转移的公平性,必须保证pos互斥性,需要引入重量级悲观锁synchronized,这会导致该段代码的并发量产生明显下降。

  应用场景:适合每个服务器的性能都差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器无法承担过大的负载。(不能因材施教)如下图,server2和server1相比太菜了,应该少分点。

二、最少链接(Least Connections)

  由于每个请求的连接时间不一样,使用轮询或者加权轮询算法的话,可能会让一台服务器当前连接数过大,而另一台服务器的连接过小,造成负载不均衡。最少连接算法就是将请求发送给当前最少连接数的服务器上。(哇!有的对手太能打了,持久战啊,再来新的对手可不能让我来对付了,得让现在对手少的人来处理,我不行了不行了)

   例如下图中,(1, 3, 5) 请求会被发送到服务器 1,但是 (1, 3) 很快就断开连接,此时只有 (5) 请求连接服务器 1;(2, 4, 6) 请求被发送到服务器 2,只有 (2) 的连接断开,此时 (6, 4) 请求连接服务器 2。该系统继续运行时,服务器 2 会承担过大的负载。(请求456都太能打了,Server2要应对两个难处理的,如果再来新的,得让Server1来处理)

三、随机算法

 通过系统随机函数,根据后端服务器列表的大小值来随机选择其中一台进行访问。由概率统计理论可以得知,随着调用量的增大,其实际效果越来越接近于平均分配流量到每一台后端服务器,也就是轮询的效果。适用于每台服务器性能相近的情况。

 

 

 1 public class Random
 2 {
 3     public static String getServer()
 4     {
 5         // 重建一个Map,避免服务器的上下线导致的并发问题
 6         Map<String, Integer> serverMap = new HashMap<String, Integer>();
 7         serverMap.putAll(IpMap.serverWeightMap);
 8         
 9         // 取得Ip地址List
10         Set<String> keySet = serverMap.keySet();
11         ArrayList<String> keyList = new ArrayList<String>();
12         keyList.addAll(keySet);
13         
14         java.util.Random random = new java.util.Random();
15         int randomPos = random.nextInt(keyList.size());
16         
17         return keyList.get(randomPos);
18     }
19 }

 

  整体代码思路和轮询法一致,先重建serverMap,再获取到server列表。在选取server的时候,通过Random的nextInt方法取0~keyList.size()区间的一个随机值,从而从服务器列表中随机获取到一台服务器地址进行返回。基于概率统计的理论,吞吐量越大,随机算法的效果越接近于轮询算法的效果

 

四、源地址哈希法

  源地址哈希通过对客户端 IP 计算哈希值之后,再对服务器数量取模得到目标服务器的序号。可以保证同一 IP 的客户端的请求会转发到同一台服务器上,用来实现会话粘滞(Sticky Session)。(我就是来报仇的,不着别人就找你)

 

 1 public class Hash
 2 {
 3     public static String getServer()
 4     {
 5         // 重建一个Map,避免服务器的上下线导致的并发问题
 6         Map<String, Integer> serverMap = new HashMap<String, Integer>();
 7         serverMap.putAll(IpMap.serverWeightMap);
 8         
 9         // 取得Ip地址List
10         Set<String> keySet = serverMap.keySet();
11         ArrayList<String> keyList = new ArrayList<String>();
12         keyList.addAll(keySet);
13         
14         // 在Web应用中可通过HttpServlet的getRemoteIp方法获取
15         String remoteIp = "127.0.0.1";
16         int hashCode = remoteIp.hashCode();
17         int serverListSize = keyList.size();
18         int serverPos = hashCode % serverListSize;
19         
20         return keyList.get(serverPos);
21     }
22 }

   前两部分和轮询法、随机法一样就不说了,差别在于路由选择部分。通过客户端的ip也就是remoteIp,取得它的Hash值,对服务器列表的大小取模,结果便是选用的服务器在服务器列表中的索引值。

  源地址哈希法的优点在于:保证了相同客户端IP地址将会被哈希到同一台后端服务器,直到后端服务器列表变更。根据此特性可以在服务消费者与服务提供者之间建立有状态的session会话

  源地址哈希算法的缺点在于:除非集群中服务器的非常稳定,基本不会上下线,否则一旦有服务器上线、下线,那么通过源地址哈希算法路由到的服务器是服务器上线、下线前路由到的服务器的概率非常低,如果是session则取不到session,如果是缓存则可能引发"雪崩"

五、加权轮询、加权最少连接、加权随机算法(Weighted ~)

  加权是针对每个服务器性能不一样而区别对待,为服务器赋予一定的权值,性能高的服务器分配更高的权值。能力越大责任越大。下面举例加权轮询,加权最少连接和加权随机实现原理、方法类似。

  加权轮询是在轮询的基础上,根据服务器的性能差异,

(服务器Server1能一打五,所以来了6个请求时,照顾一下Server2)

 1 public class WeightRoundRobin
 2 {
 3     private static Integer pos;
 4     
 5     public static String getServer()
 6     {
 7         // 重建一个Map,避免服务器的上下线导致的并发问题
 8         Map<String, Integer> serverMap = new HashMap<String, Integer>();
 9         serverMap.putAll(IpMap.serverWeightMap);
10         
11         // 取得Ip地址List
12         Set<String> keySet = serverMap.keySet();
13         Iterator<String> iterator = keySet.iterator();
14         //与轮询法类似,只是在获取服务器地址之前增加了一段权重计算的代码,根据权重的大小,
      //将地址重复地增加到服务器地址列表中,权重越大,该服务器每轮所获得的请求数量越多。
15 List<String> serverList = new ArrayList<String>(); 16 while (iterator.hasNext()) 17 { 18 String server = iterator.next(); 19 int weight = serverMap.get(server); 20 for (int i = 0; i < weight; i++) 21 serverList.add(server); 22 } 23 24 String server = null; 25 synchronized (pos) 26 { 27 if (pos > keySet.size()) 28 pos = 0; 29 server = serverList.get(pos); 30 pos ++; 31 } 32 33 return server; 34 } 35 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

参考资料连接

posted @ 2020-01-02 18:16  Qmillet  阅读(305)  评论(0编辑  收藏  举报