zoukankan      html  css  js  c++  java
  • 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 }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    参考资料连接

  • 相关阅读:
    [GEF]实现模板功能
    一篇WTP文章
    写代码的代码:JET
    投票选择eclipse.org的新界面
    在SWT里显示AWT对象
    Plugin.xml > Manifest.mf
    关于本体编程的实现
    一个用OWLS组装Web服务的例子
    感受Ruby on Rails
    通过OCP考试
  • 原文地址:https://www.cnblogs.com/qmillet/p/12125626.html
Copyright © 2011-2022 走看看