负载均衡,英文名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。通过某种负载分担技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。负载均衡能够平均分配客户请求到服务器阵列,借此提供快速获取重要数据,解决大量并发访问服务问题,这种集群技术可以用最少的投资获得接近于大型主机的性能
在介绍几种算法前先准备工作
package tim.wang.sourcecode.loadblance;
public class ServerInfos {
private static final List<String> ipList =
Stream.of("192.168.0.31", "192.168.0.92", "192.168.0.12", "192.168.0.43", "192.168.0.100")
.collect(Collectors.toList());
private static Map<String, Integer> ipWeightList = new HashMap<>();
static {
ipWeightList.put("192.168.0.31", 1);
ipWeightList.put("192.168.0.92", 5);
ipWeightList.put("192.168.0.12", 2);
ipWeightList.put("192.168.0.43", 4);
}
static Map<String, Integer> getIpWeightList() {
return ipWeightList;
}
static List<String> getIpList() {
return ipList;
}
}
一、随机算法
通过系统的随机函数,根据后端服务器列表的大小来随机获取其中的一台来访问,随着调用量的增大,实际效果越来越近似于平均分配到没一台服务器.和轮询的效果类似.
public class RandomLoadBlance {
/**
* 简单的随机负载均衡算法
* @return ip
*/
public static String getIp() {
List<String> ipList = ServerInfos.getIpList();
int randomIndex = new Random().nextInt(ipList.size());
return ipList.get(randomIndex);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(getIp());
}
}
}
二、随机权重算法
单纯的随机算法通过伪随机数来保证请求均匀的分布到对应的Server上,但是其忽略了每一个服务器处理能力的差异,这样就导致处理能力差的服务可能因为这种绝对的均衡策略而崩掉,改进策略就是根据权重占比随机。算法很简单,就是一根数轴。然后利用伪随机数产生点,看点落在了哪个区域从而选择对应的Server
。
public class RandomLoadBlanceV2 {
/**
* 带权重的随机算法
* 生成一个随机数
* 权重类似一个线段上的点,例如5,1,2,3对应的线段就是
* ____5_6__8__11
* 就看小于11的随机数落在那个线段区间内,就是哪个ip
* @return
*/
public static String getIp() {
Map<String, Integer> ipWeightList = ServerInfos.getIpWeightList();
int sumWeight = ipWeightList.values().stream().mapToInt(Integer::intValue).sum();
int randomWeight = new Random().nextInt(sumWeight);
for (Map.Entry<String, Integer> entry : ipWeightList.entrySet()) {
if (randomWeight < entry.getValue()) {
return entry.getKey();
}
randomWeight -= entry.getValue();
}
return null;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(getIp());
}
}
}
三、轮询算法
轮询的策略目的在于请求的绝对均衡,但是在实际的情况下,可能服务器并不是完全一样。导致有些性能高的服务器不能完全发挥出来.
public class RoundLoadBlance {
private static int index = 0;
/**
* 简单的轮询负载均衡算法,不带权重,index++即可
* @return ip
*/
public static String getIp() {
List<String> ipList = ServerInfos.getIpList();
if (index > ipList.size() - 1) {
index = 0;
}
return ipList.get(index++);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(getIp());
}
}
}
四、轮询权重算法
public class RoundLoadBlanceV2 {
private static int index = 0;
/**
* 轮询负载均衡算法,带权重
* 也类似随机的randomloadblancev2
* 即把权重变成一个线段,然后下标从0一直移动到队尾
* @return ip
*/
public static String getIp() {
Map<String, Integer> ipWeightList = ServerInfos.getIpWeightList();
int sumWeight = ipWeightList.values().stream().mapToInt(Integer::intValue).sum();
if (index > sumWeight) {
index = 0;
}
int currentIndex = index;
for (Map.Entry<String, Integer> entry : ipWeightList.entrySet()) {
if (currentIndex <= entry.getValue()) {
index++;
return entry.getKey();
}
currentIndex -= entry.getValue();
}
return null;
}
public static void main(String[] args) {
for (int i = 0; i < 30; i++) {
System.out.println(getIp());
}
}
}
五、平滑轮询加权
对于{a:5, b:1, c:1)
这三个服务实例,权重轮询会得到{ a a a a a b c }
这样的访问顺序,那么当权重差过大时,对于服务器a
来说依然存在集中访问,为了解决这个问题,Nginx实现了一种平滑的轮询算法,对于上述权重实例,Nginx的算法得出的访问顺序为{ a, a, b, a, c, a, a }
,这样的分布显然比直接轮询合理的多。
整个实现非常巧妙,大概思想是每一个Server
的权重都是动态可改变的,在遍历过程中对每一个Server
的权重做累加,然后选出权重最高的作为best,选中后再对best做降权,以此达到平滑。
以{a:5, b:1, c:1)
作为输入,选择10次,其输出结果为{ a a c a b a c a b a }
,下面是部分详情,帮助理解加权与降权的流程。
server name: a weight: 5 current: 5
server name: b weight: 2 current: 2
server name: c weight: 3 current: 3
Server(address=a, weight=5) // 第一次选择
server name: a weight: 5 current: 0
server name: b weight: 2 current: 4
server name: c weight: 3 current: 6
Server(address=a, weight=5) // 第二次选择
server name: a weight: 5 current: 5
server name: b weight: 2 current: 6
server name: c weight: 3 current: 1
Server(address=c, weight=3) // 第三次选择
server name: a weight: 5 current: 0
server name: b weight: 2 current: 8
server name: c weight: 3 current: 4
Server(address=a, weight=5) // 第四次选择
server name: a weight: 5 current: 5
server name: b weight: 2 current: 0
server name: c weight: 3 current: 7
Server(address=b, weight=2) // 第五次选择
六、一致性哈希算法
七、最少活跃数
https://juejin.im/entry/5b13e712e51d4506da5a039b
https://www.jianshu.com/p/40e196414cfa
https://segmentfault.com/a/1190000004492447