轮询调度算法的原理是每一次把来自用户的请求轮流分配给内部中的服务器,从1开始,直到N(内部服务器个数),然后重新开始循环。算法的优点是其简洁性,它无需记录当前所有连接的状态,所以它是一种无状态调度。
其代码实现大致如下:
import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import java.util.Set;/**
* @author ashang.peng@aliyun.com
* @date 二月 07, 2017
*/class RoundRobin {
private static Integer pos = 0;
public static String getServer()
{ // 重建一个Map,避免服务器的上下线导致的并发问题
Map<String, Integer> serverMap = new HashMap<String, Integer>();
serverMap.putAll(IpMap.serverWeightMap); // 取得Ip地址List
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<String>();
keyList.addAll(keySet); String server = null;
synchronized (pos)
{ if (pos > keySet.size())
pos = 0;
server = keyList.get(pos);
pos ++;
} return server;
}
}
由于serverWeightMap中的地址列表是动态的,随时可能有机器上线、下线或者宕机,因此为了避免可能出现的并发问题,方法内部要新建局部变量serverMap,现将serverMap中的内容复制到线程本地,以避免被多个线程修改。这样可能会引入新的问题,复制以后serverWeightMap的修改无法反映给serverMap,也就是说这一轮选择服务器的过程中,新增服务器或者下线服务器,负载均衡算法将无法获知。新增无所谓,如果有服务器下线或者宕机,那么可能会访问到不存在的地址。因此,服务调用端需要有相应的容错处理,比如重新发起一次server选择并调用。
对于当前轮询的位置变量pos,为了保证服务器选择的顺序性,需要在操作时对其加锁,使得同一时刻只能有一个线程可以修改pos的值,否则当pos变量被并发修改,则无法保证服务器选择的顺序性,甚至有可能导致keyList数组越界。
轮询法的优点在于:试图做到请求转移的绝对均衡。
轮询法的缺点在于:为了做到请求转移的绝对均衡,必须付出相当大的代价,因为为了保证pos变量修改的互斥性,需要引入重量级的悲观锁synchronized,这将会导致该段轮询代码的并发吞吐量发生明显的下降。
通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,
其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。
随机法的代码实现大致如下:
import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import java.util.Set;/**
* @author ashang.peng@aliyun.com
* @date 二月 07, 2017
*/
class Random {
public static String getServer()
{ // 重建一个Map,避免服务器的上下线导致的并发问题
Map<String, Integer> serverMap = new HashMap<String, Integer>();
serverMap.putAll(IpMap.serverWeightMap); // 取得Ip地址List
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<String>();
keyList.addAll(keySet);
java.util.Random random = new java.util.Random();
int randomPos = random.nextInt(keyList.size()); return keyList.get(randomPos);
}
}
整体代码思路和轮询法一致,先重建serverMap,再获取到server列表。在选取server的时候,通过Random的nextInt方法取0~keyList.size()区间的一个随机值,从而从服务器列表中随机获取到一台服务器地址进行返回。基于概率统计的理论,吞吐量越大,随机算法的效果越接近于轮询算法的效果。