1. 简介
微服务场景,通常通过多服务器实例实现应用的水平扩展,借助负载均衡器,实现接入点的无感知接入。负载均衡器的核心就是负载均衡算法,通过一定的逻辑将前端请求,均匀、高效的分发到后端应用实例。当我们使用市面上成熟的负载均衡器时,根据需求配置合理的负载均衡算法也是核心的配置之一,这些都需要对负载均衡算法的深入理解。下面我们将常见的负载均衡算法罗列,并简化实现算法的核心逻辑。
2. 常见算法
2.1 轮询法
将请求按顺序轮流地分配到后端服务器上。均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
2.2 随机法
通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配,也就是轮询的结果。
2.3 源地址哈希法
源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。这种算法可以用于会话保持,避免会话共享的集成。
2.4 加权轮询法
不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
2.5 加权随机法
与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
2.6 最小连接数法
最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。
3. 算法实现
负载均衡算法本身可以简化为一个从服务器列表获取一个服务器ip,同时为了兼容加权算法,所以这里的实现就直接简化为通过一个ip为key,权重为value的Map。接口定义如下:
1 2 3 4 5 6
| public interface IBalanceAlg {
String execute(Map<String, Integer> addMap); }
|
下面章节即为具体算法的实现。
3.1 轮询算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class RoundRobin implements IBalanceAlg { private static Integer pos=0;
@Override public String execute(Map<String, Integer> addMap) { Map<String,Integer> serverMap=new HashMap<String,Integer>(); serverMap.putAll(addMap); Set<String> serviceSet=serverMap.keySet(); List<String> serviceList=new ArrayList<String>(); serviceList.addAll(serviceSet); String server=null; synchronized (pos) { if(pos>=serviceList.size()){ pos=0; } server=serviceList.get(pos); pos++; } return server; } }
|
3.2 随机算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class RandomSelect implements IBalanceAlg {
@Override public String execute(Map<String, Integer> addMap) { Map<String, Integer> serverMap = new HashMap<String, Integer>(); serverMap.putAll(addMap); Set<String> serviceSet = serverMap.keySet(); List<String> serviceList = new ArrayList<String>(); serviceList.addAll(serviceSet); Random random=new Random(); int rvalue=random.nextInt(serviceList.size()); String server = serviceList.get(rvalue); return server; } }
|
3.3 带权重都轮询算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
public class WeightRoundRobin implements IBalanceAlg { private static Integer pos=0;
@Override public String execute(Map<String, Integer> addMap) { Map<String,Integer> serverMap=new HashMap<String,Integer>(); serverMap.putAll(addMap); List<String> serviceList=new ArrayList<String>(); for(Map.Entry<String, Integer> ent:serverMap.entrySet()){ String service=ent.getKey(); Integer weight=ent.getValue(); for(int i=0;i<weight;i++){ serviceList.add(service); } } String server=null; synchronized (pos) { if(pos>=serviceList.size()){ pos=0; } server=serviceList.get(pos); pos++; } return server; } }
|
3.4 带权重都随机算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class WeightRandom implements IBalanceAlg {
@Override public String execute(Map<String, Integer> addMap) { Map<String,Integer> serverMap=new HashMap<String,Integer>(); serverMap.putAll(addMap); List<String> serviceList=new ArrayList<String>(); for(Map.Entry<String, Integer> ent:serverMap.entrySet()){ String service=ent.getKey(); Integer weight=ent.getValue(); for(int i=0;i<weight;i++){ serviceList.add(service); } } Random random=new Random(); int rvalue=random.nextInt(serviceList.size()); String server = serviceList.get(rvalue); return server; }
}
|
3.5 源地址hash算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class ConsumerHash {
public String execute(Map<String, Integer> addMap, String remoteIp) { Map<String, Integer> serverMap = new HashMap<String, Integer>(); serverMap.putAll(addMap); Set<String> serviceSet = serverMap.keySet(); List<String> serviceList = new ArrayList<String>(); serviceList.addAll(serviceSet); int hashCode=remoteIp.hashCode(); int servicePos=hashCode%serviceList.size();
return serviceList.get(servicePos); } }
|
| 最小连接数算法,需要记录到各个服务器的存活的连接数,这里不做实现。