1. 介绍
负载均衡就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行。比如对于某个请求,使用负载均衡能选择合适的服务器去响应。
2. 算法实现
自定义一个Server类
public class Server {
private String ip;
private int weight;
private int currentWeight = 0;
public Server(String ip) {
this.ip = ip;
}
public Server(String ip, int weight) {
this.ip = ip;
this.weight = weight;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getWeight() {
return weight;
}
public int getCurrentWeight() {
return currentWeight;
}
public void setCurrentWeight(int currentWeight) {
this.currentWeight = currentWeight;
}
@Override
public String toString() {
return "Server{" +
"ip='" + ip + '\'' +
", weight=" + weight +
'}';
}
}
在测试类中定义如下变量,分别模拟无权重的服务器和有权重的服务器
private static Server[] IP = {
new Server("192.168.0.1"),
new Server("192.168.0.2"),
new Server("192.168.0.3"),
new Server("192.168.0.4"),
new Server("192.168.0.5"),
new Server("192.168.0.6"),
new Server("192.168.0.7"),
new Server("192.168.0.8"),
new Server("192.168.0.9"),
new Server("192.168.0.10")
};
private static int TOTAL_WEIGHT = 0;
private static Server[] IP_WITH_WEIGHT = {
new Server("192.168.0.1", 5),
new Server("192.168.0.2", 1),
new Server("192.168.0.3", 1)
};
static {
for (int i = 0; i < IP_WITH_WEIGHT.length; i++) {
TOTAL_WEIGHT += IP_WITH_WEIGHT[i].getWeight();
}
}
2.1 随机算法
2.1.1 无权重
public static Server random() {
Random random = new Random();
return IP[random.nextInt(10)];
}
2.1.2 有权重
思路如下:求出所有权重的和,随机一个数,当随机数落在权重之和的某个区间时,则返回该区间对应的IP。类似于在一条线段上随机置点,获取该点所在区间。
public static Server randomWithWeight() {
int randomWeight = new Random().nextInt(TOTAL_WEIGHT);
for (int i = 0; i < IP_WITH_WEIGHT.length; i++) {
int weight = IP_WITH_WEIGHT[i].getWeight();
if (randomWeight < weight) {
return IP_WITH_WEIGHT[i];
} else {
randomWeight -= weight;
}
}
return null;
}
2.2 轮询算法
2.2.1 无权重
private static int INDEX = 0;
public static Server roundRobin() {
if (INDEX == IP.length) {
INDEX = 0;
}
return IP[INDEX++];
}
2.2.2 有权重
Nginx的默认算法。
public static Server roundRobinWithWeight() {
// 1.所有服务器的currentWeight都加上各自的weight
for (int i = 0; i < IP_WITH_WEIGHT.length; i++) {
int currentWeight = IP_WITH_WEIGHT[i].getCurrentWeight();
int weight = IP_WITH_WEIGHT[i].getWeight();
IP_WITH_WEIGHT[i].setCurrentWeight(currentWeight + weight);
}
// 2.找出currentWeight最大的那个服务器,并将其currentWeight减去所有weight的和
Server max = IP_WITH_WEIGHT[0];
for (int i = 1; i < IP_WITH_WEIGHT.length; i++) {
if (max.getCurrentWeight() < IP_WITH_WEIGHT[i].getCurrentWeight()) {
max = IP_WITH_WEIGHT[i];
}
}
max.setCurrentWeight(max.getCurrentWeight() - TOTAL_WEIGHT);
return max;
}
2.3 一致性哈希算法
该算法的目的是,同一个用户只会访问到同一台服务器,算法介绍可看这篇文章。
public class ConsistentHash {
private static Server[] IP = {
new Server("192.168.0.1"),
new Server("192.168.0.2"),
new Server("192.168.0.3"),
new Server("192.168.0.4"),
new Server("192.168.0.5"),
new Server("192.168.0.6"),
new Server("192.168.0.7"),
new Server("192.168.0.8"),
new Server("192.168.0.9"),
new Server("192.168.0.10")
};
private static TreeMap<Long, Server> virtualNode = new TreeMap<>();
/**
* 每个服务器虚拟节点的数量
*/
private static final int NODE_SIZE = 100;
static {
for (Server server : IP) {
for (int i = 0; i < NODE_SIZE; i++) {
long hash = getHash(server.getIp() + i);
virtualNode.put(hash, server);
}
}
}
public static void main(String[] args) {
System.out.println(getServer("10.16.129.67"));
System.out.println(getServer("100.19.129.68"));
System.out.println(getServer("10.16.129.67"));
}
/**
* 获取客户端要访问的服务器
*/
private static Server getServer(String client) {
long clientHash = getHash(client);
// 获取红黑树中大于"客户端哈希值"的子树的第一个节点
SortedMap<Long, Server> subTree = virtualNode.tailMap(clientHash);
Long firstKey;
if (null == subTree) {
firstKey = virtualNode.firstKey();
} else {
firstKey = subTree.firstKey();
}
return virtualNode.get(firstKey);
}
/**
* 计算哈希值
*/
private static long getHash(String str) {
final int p = 16777619;
long hash = 2166136261L;
for (int i = 0; i < str.length(); i++) {
hash = (hash ^ str.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
if (hash < 0) {
hash = Math.abs(hash);
}
return hash;
}
}