一、随机算法
1. 简单随机算法
思路:获取随机数,随机数不大于服务集合的容量,将该随机数当作下标,获取IP
// 服务IP集合
private static List<String> serverIps = Arrays.asList("A", "B", "C", "D", "E");
public static String random(){
Random random = new Random();
int i = random.nextInt(serverIps.size());
return serverIps.get(i);
}
2. 加权随机算法
思路:创建一个新的List,将含有权重值得IP重新存入集合,权重是多少,就存多少个,然后使用随机算法从改集合中获取一个IP
public static String random() {
// 含有权重的服务IP集合
Map<String, Integer> map = new HashMap<>();
map.put("A", 4);
map.put("B", 6);
map.put("C", 3);
map.put("D", 2);
map.put("E", 5);
List<String> ips = new ArrayList<>();
// 遍历map
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
// 服务IP
String ip = entry.getKey();
// 权重值
Integer integer = entry.getValue();
// 循环添加IP
for (int i = 0; i<integer; i++){
ips.add(ip);
}
}
Random random = new Random();
int i = random.nextInt(ips.size());
return ips.get(i);
}
3.加权随机算法(优化)
思路:算法2中如果权重值过大,新建的List值就会很多,严重消耗内存,因此通过比较大小的方式确定随机数落在那个服务上;将各个服务的权重值看做数轴上的点,通过判断随机数落在那个区间内,从而确定选哪个服务
public static String random() {
// 含有权重的服务IP集合
Map<String, Integer> map = new HashMap<>();
map.put("A", 4);
map.put("B", 6);
map.put("C", 3);
map.put("D", 2);
map.put("E", 5);
// 总权重
Integer totalWeigh = 0;
Collection<Integer> values = map.values();
for (Integer value : values) {
totalWeigh += value;
}
// 获取随机数
Random random = new Random();
Integer pos = random.nextInt(totalWeigh);
// 遍历含权重的服务集合
for (String ip : map.keySet()) {
// 获取IP权重值
Integer integer = map.get(ip);
// 如果随机数小于权重值,说明改随机数落在改服务上
if (pos < integer) {
return ip;
}
pos = pos - integer;
}
return "";
}
二、轮询算法
1. 简单轮询
思路1:设置全局下标,每次请求都+1
// 服务IP集合
private static List<String> serverIps = Arrays.asList("A", "B", "C", "D", "E");
// 轮询下标
private static int pos = 0;
public static String roundRobin() {
if (pos >= serverIps.size()) {
pos = 0;
}
String ip = serverIps.get(pos);
pos++;
return ip;
}
思路2:通过取模,获取下标
// 服务IP集合
private static List<String> serverIps = Arrays.asList("A", "B", "C", "D", "E");
// 原子计数器
private static AtomicInteger atomicNum = new AtomicInteger(0);
public static String roundRobin() {
int num = atomicNum.getAndAdd(1);
int inx = num % serverIps.length;
return serverIps[inx];
}
2. 加权轮询(List算法)
思路:创建新List,根据权重值将IP添加到新List中,然后通过简单轮询获取IP。
缺点:当权重值过大时,list过大,严重消耗内存
// 轮询下标
private static int pos = 0;
public static String roundRobin() {
// 含有权重的服务IP集合
Map<String, Integer> map = new HashMap<>();
map.put("A", 4);
map.put("B", 6);
map.put("C", 3);
map.put("D", 2);
map.put("E", 5);
List<String> ipList = new ArrayList<>();
// 遍历map
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
// 服务IP
String ip = entry.getKey();
// 权重值
Integer integer = entry.getValue();
// 循环添加IP
for (int i = 0; i < integer; i++) {
ipList.add(ip);
}
}
if (pos >= ipList.size()) {
pos = 0;
}
String ip = ipList.get(pos);
pos++;
return ip;
}
3.加权轮询(取模坐标轴算法)
思路:记录请求次数,用请求次数模上总权重,然后判断该值落在坐标轴上的哪个范围
/**
* 请求计数器
*/
private static AtomicInteger atomicNum = new AtomicInteger(0);
public static String roundRobin() {
// 含有权重的服务IP集合
Map<String, Integer> map = new HashMap<>();
map.put("A", 4);
map.put("B", 6);
map.put("C", 3);
map.put("D", 2);
map.put("E", 5);
// 获取总权重
Integer totalWeigh = 0;
Collection<Integer> values = map.values();
for (Integer value : values) {
totalWeigh += value;
}
// 获取请求计数器值
int pos = atomicNum.getAndAdd(1);
// 取模
int inx = pos % totalWeigh;
// 循环判断,如果余数小于IP的权重值,就返回IP
for (String ip : map.keySet()) {
Integer weigh = map.get(ip);
if (inx < weigh){
return ip;
}
inx = inx - weigh;
}
return "";
}
4. 平滑加权轮询算法
思路:引入静态权重、动态权重概念
假设现在有3台服务:
服务器 | 权重 | 动态权重
—|--- | —
A | 5 | 0
B | 1 | 0
C | 1 | 0
- IP
- weigh --静态权重
- currentWeigh --动态权重
算法介绍:
说明 | 算法公式 |
1. 计算总权重totalWeigh | totalWeigh = weigh + … + weigh |
2.计算currentWeigh值,初始为0 | currentWeigh = currentWeigh + weigh |
3.获取currentweigh最大值 | MAX(currentWeigh ) |
4.返回IP | 返回currentweigh最大值对应的服务IP |
5.修改最大权重值 currentWeigh - 总权重,其它不变 | MAX(currentWeigh ) - totalWeigh |
运行结果:
运行次数 | 计算currentWeigh值 | 获取currentweigh最大值 | 返回IP | 修改最大currentWeigh |
1 | 5,1,1 | 5 | A | -2,1,1 |
2 | 3,2,2 | 3 | A | -4,2,2 |
3 | 1,3,3 | 3 | B | 1,-4,3 |
4 | 6,-3,4 | 6 | A | -1,-3,4 |
5 | 4,-2,5 | 5 | C | 4,-2,-2 |
6 | 9,-1,-1 | 9 | A | 2,-1,-1 |
7 | 7,0,0 | 7 | A | 0,0,0 |
代码实例
- 设置服务集合
/**
* 服务
*/
public class ServerIp {
// 含有权重的服务IP集合
public static Map<String, Integer> serverMap = new HashMap<>();
static {
serverMap.put("A", 5);
serverMap.put("B", 1);
serverMap.put("C", 1);
}
}
- 封装权重对象
/**
* 权重对象
*/
public class Weigh {
/**
* 服务IP
*/
private String ip;
/**
* 静态权重
*/
private Integer weigh;
/**
* 动态权重
*/
private Integer currentWeigh;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Integer getWeigh() {
return weigh;
}
public void setWeigh(Integer weigh) {
this.weigh = weigh;
}
public Integer getCurrentWeigh() {
return currentWeigh;
}
public void setCurrentWeigh(Integer currentWeigh) {
this.currentWeigh = currentWeigh;
}
public Weigh(String ip, Integer weigh, Integer currentWeigh) {
this.ip = ip;
this.weigh = weigh;
this.currentWeigh = currentWeigh;
}
- 负载算法
public class RoundRobin {
// 服务集合
private static List<Weigh> weighList = new ArrayList<>();
// 总权重
private static Integer totalWeigh = 0;
public static String getServer() {
// 1、初始化Weigh对象,currentWeigh值设置为0
if (weighList.isEmpty()) {
ServerIp.serverMap.forEach((ip, weigh) -> {
Weigh w = new Weigh(ip, weigh, 0);
weighList.add(w);
totalWeigh +=weigh;
});
}
// 2、循环计算currentWeigh值,公式:currentWeigh = weigh + currentWeigh
for (Weigh weigh : weighList) {
weigh.setCurrentWeigh(weigh.getWeigh() + weigh.getCurrentWeigh());
}
// 3、获取currentweigh最大值
Weigh maxWeigh = null;
for (Weigh weigh : weighList) {
if (maxWeigh == null || weigh.getCurrentWeigh() > maxWeigh.getCurrentWeigh()) {
maxWeigh = weigh;
}
}
// 修改最大权重值,公式:currentWeigh - 总权重
maxWeigh.setCurrentWeigh(maxWeigh.getCurrentWeigh() - totalWeigh);
return maxWeigh.getIp();
}
}