负载均衡算法
总结自b站up主IT老哥视频
概述
常见的负载均衡算法有随机、加权随机、轮询、加权轮询、平滑加权轮询、一致性哈希
算法
- 服务器IP
public class ServerIps {
public static final List<String> LIST = Arrays.asList(
"A",
"B",
"C"
);
//带权重
public static final Map<String,Integer> WEIGHT_LIST = new LinkedHashMap<String, Integer>();
static {
WEIGHT_LIST.put("A",2);
WEIGHT_LIST.put("B",3);
WEIGHT_LIST.put("C",5);
}
}
随机
- 随机就是将请求随机转发到一台服务器上
//随机
public class RandomSelect {
public static String getServer() {
Random random = new Random();
int rand = random.nextInt(ServerIps.LIST.size());
return ServerIps.LIST.get(rand);
}
public static void main(String[] args) {
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
}
}
测试
轮询
- 轮询就是将请求依次转发到每个服务器上
public class RoundRobin {
//位置
public static Integer pos = 0;
public static String getServer() {
String ip = null;
synchronized (pos) {
if(pos >= ServerIps.LIST.size()) {
pos = 0;
}
ip = ServerIps.LIST.get(pos);
pos++;
}
return ip;
}
public static void main(String[] args) {
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
}
}
测试
加权随机
- 加权随机就是每台服务器被随机到的概率不同
实现方式一
- 利用集合依次按权重将服务器ip添加到集合中,然后生成一个随机数(<集合大小),然后获取随机数索引处的服务器,将请求转发到该服务器
//缺点 权重大 ips越大 占内存 带权重随机
public class WeightRandom {
public static String getServer() {
//生成随机数作为List下标
List<String> ips = new ArrayList<>();
for (String ip: ServerIps.WEIGHT_LIST.keySet()) {
Integer weight = ServerIps.WEIGHT_LIST.get(ip);
//weight多少 在ips里面存多少 例 A 权重为2 在ips里面存两个
for (int i = 0; i < weight ; i++) {
ips.add(ip);
}
}
Random random = new Random();
int randomPos = random.nextInt(ips.size());
return ips.get(randomPos);
}
public static void main(String[] args) {
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
}
}
测试
实现方式二
执行流程:
- 获取服务器的总权重
- 然后获取随机数(小于总权重)
- 依次遍历服务器,如果随机数大于当前服务器的weight,则随机数减去weight,否则将请求转发到该服务器
//带权重随机
public class WeightRandomV2 {
public static String getServer() {
//总权重
int totalWeights = ServerIps.WEIGHT_LIST.values().stream().mapToInt(w -> w).sum();
Random random = new Random();
int pos = random.nextInt(totalWeights);
for(String ip: ServerIps.WEIGHT_LIST.keySet()) {
Integer weight = ServerIps.WEIGHT_LIST.get(ip);
if(pos < weight) {
return ip;
}
pos = pos - weight;
}
return "";
}
public static void main(String[] args) {
for (int i=0 ; i<10 ; i++) {
System.out.println(getServer());
}
}
}
测试
加权轮询
- 和加权随机类似
实现方式一
- 遍历服务器ip,使用一个集合,将每个服务器添加到集合中对应的weight(权重)次,
- 定义变量保存访问次数count,每次返回list.get(count%list.size())的服务器
//缺点 权重大 ips越大 占内存 带权重轮询
public class WeightRoundRobinV0 {
//被访问的次数
static int count = 0;
public static String getServer() {
//生成随机数作为List下标
List<String> ips = new ArrayList<>();
for (String ip: ServerIps.WEIGHT_LIST.keySet()) {
Integer weight = ServerIps.WEIGHT_LIST.get(ip);
//weight多少 在ips里面存多少 例 A 权重为2 在ips里面存两个
for (int i = 0; i < weight ; i++) {
ips.add(ip);
}
}
String ip = ips.get(count % ips.size());
++count;
return ip;
}
public static void main(String[] args) {
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
System.out.println(getServer());
}
}
测试
实现方式二
- 与加权随机类似
//轮询优化
public class WeightRoundRobin {
public static String getServer(Integer num) {
int totalWeights = ServerIps.WEIGHT_LIST.values().stream().mapToInt(w -> w).sum();
Integer pos = num % totalWeights;
for(String ip: ServerIps.WEIGHT_LIST.keySet()) {
Integer weight = ServerIps.WEIGHT_LIST.get(ip);
if(pos < weight) {
return ip;
}
pos = pos - weight;
}
return "";
}
public static void main(String[] args) {
for (int i=0 ; i<10 ; i++) {
System.out.println(getServer(i));
}
}
}
测试
平滑加权轮询
- 上面的加权轮询会连续访问一个服务器多次,我们可以优化一下,把请求分散一下,不要一直访问一个服务器
- 每次请求返回下图运算中的result
public class WeightRoundRobinV2 {
public static Map<String,Weight> currWeights = new HashMap<>();
public static String getServer() {
int totalWeights = ServerIps.WEIGHT_LIST.values().stream().mapToInt(w -> w).sum();
//currentWeight 默认值 0,currentWeight初始化
if(currWeights.isEmpty()) {
ServerIps.WEIGHT_LIST.forEach((ip,weight)->
{
currWeights.put(ip,new Weight(ip,weight,0));
});
}
//遍历动态权重,动态权重加上当前权重
for(Weight weight: currWeights.values()) {
weight.setCurrentWeight(weight.getCurrentWeight() + weight.getWeight());
}
//找最大值
Weight maxCurrentWeight = null;
for(Weight weight: currWeights.values()) {
if(maxCurrentWeight == null || weight.getCurrentWeight() > maxCurrentWeight.getCurrentWeight()) {
maxCurrentWeight = weight;
}
}
maxCurrentWeight.setCurrentWeight( maxCurrentWeight.getCurrentWeight() - totalWeights);
return maxCurrentWeight.getIp();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(getServer());
}
}
}
测试
- 通过运行结果我们可以知道成功地根据权重把请求分散到各个服务器上。(C5,B3,A2)
一致性hash
推荐一篇通俗易懂的文章: https://www.cnblogs.com/lpfuture/p/5796398.html
- 使用TreeMap(使用了红黑树的数据结构,有序的map)来模拟在hash环上获取一个节点