负载均衡算法
目录
随机负载
随机从服务列表中挑选一个服务
// 基于Java的随机负载均衡算法
public class RandomLoadBalancer {
// 传入服务器列表,返回随机选择的服务器
public Server selectServer(List<Server> serverList) {
// 获取服务器列表大小
int size = serverList.size();
// 生成随机数
int randomIndex = new Random().nextInt(size);
// 返回随机选择的服务器
return serverList.get(randomIndex);
}
// 服务器类
public static class Server {
private String ip;
private int port;
public Server(String ip, int port) {
this.ip = ip;
this.port = port;
}
// getter和setter方法省略
}
}
轮询负载
遍历服务列表(排好队一个一个来)
public class RoundRobinLoadBalancer {
// 记录上一次选择的服务器下标
private int lastIndex = -1;
// 传入服务器列表,返回轮询选择的服务器
public Server selectServer(List<Server> serverList) {
// 获取服务器列表大小
int size = serverList.size();
// 如果服务器列表为空,返回null
if (size == 0) {
return null;
}
// 如果只有一个服务器,直接返回该服务器
if (size == 1) {
return serverList.get(0);
}
// 上锁,保证线程安全
synchronized (this) {
// 如果上一次选择的服务器下标超出了服务器列表大小,重置为-1
if (lastIndex >= size) {
lastIndex = -1;
}
// 选择下一个服务器
int index = lastIndex + 1;
lastIndex = index;
// 返回选择的服务器
return serverList.get(index);
}
}
// 服务器类
public static class Server {
private String ip;
private int port;
public Server(String ip, int port) {
this.ip = ip;
this.port = port;
}
// getter和setter方法省略
}
}
加权负载
等于是给每个服务加了一个访问比例,又可以细分为随机加权负载、轮询加权负载,区别如下:
假设有3台服务、权重为3/5/2:
server1 3
server2 5
server3 2
随机加权负载:就是按照几率选择服务,,这里提供一个方案:
初始化一个容量为10的数组,随机装载上以上服务id,比如server2就会占有5个下标,获取服务的时候获取一个随机数0-9,取对应下标的服务,如果服务已经被取过了则下标+1,往上推,到了边界则置0再往上推,如果全被取过一次了,则重新装载
轮询加权负载:就是挨个遍历结果,这里提供一个比较好的示例:
public class WeightedRoundRobinLoadBalancer {
// 记录上一次选择的服务器下标
private int lastIndex = -1;
// 记录当前权重
private int currentWeight = 0;
// 最大权重
private int maxWeight;
// 权重的最大公约数
private int gcdWeight;
// 服务器列表
private List<Server> serverList;
public WeightedRoundRobinLoadBalancer(List<Server> serverList) {
this.serverList = serverList;
init();
}
// 初始化
private void init() {
// 获取最大权重
maxWeight = getMaxWeight();
// 获取权重的最大公约数
gcdWeight = getGcdWeight();
}
// 传入服务器列表,返回加权轮询选择的服务器
public Server selectServer() {
while (true) {
// 上一次选择的服务器下标加1
lastIndex = (lastIndex + 1) % serverList.size();
// 如果上一次选择的服务器下标为0,重新计算当前权重
if (lastIndex == 0) {
currentWeight = currentWeight - gcdWeight;
if (currentWeight <= 0) {
currentWeight = maxWeight;
if (currentWeight == 0) {
return null;
}
}
}
// 获取当前下标的服务器
Server server = serverList.get(lastIndex);
// 如果当前服务器的权重大于等于当前权重,返回该服务器
if (server.getWeight() >= currentWeight) {
return server;
}
}
}
// 获取最大权重
private int getMaxWeight() {
int maxWeight = 0;
for (Server server : serverList) {
int weight = server.getWeight();
if (weight > maxWeight) {
maxWeight = weight;
}
}
return maxWeight;
}
// 获取权重的最大公约数
private int getGcdWeight() {
int gcdWeight = 0;
for (Server server : serverList) {
int weight = server.getWeight();
if (gcdWeight == 0) {
gcdWeight = weight;
} else {
gcdWeight = gcd(gcdWeight, weight);
}
}
return gcdWeight;
}
// 求最大公约数
private int gcd(int a, int b) {
if (b == 0) {
return a;
} else {
return gcd(b, a % b);
}
}
// 服务器类
public static class Server {
private String ip;
private int port;
private int weight; // 权重
public Server(String ip, int port, int weight) {
this.ip = ip;
this.port = port;
this.weight = weight;
}
// getter和setter方法省略
}
public static void main(String[] args) {
List<Server> servers=new ArrayList<>();
servers.add(new Server("1", 1, 3));
servers.add(new Server("2", 2, 5));
servers.add(new Server("3", 3, 2));
WeightedRoundRobinLoadBalancer weightedRoundRobinLoadBalancer = new WeightedRoundRobinLoadBalancer(servers);
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
System.out.println(JSON.toJSON(weightedRoundRobinLoadBalancer.selectServer()));
}
}
IP_HASH负载
就是根据IP的Hash值取余
public class HashLoadBalancer {
// 服务器列表
private List<Server> serverList;
public HashLoadBalancer(List<Server> serverList) {
this.serverList = serverList;
}
// 传入客户端IP,返回选择的服务器
public Server selectServer(String clientIp) {
// 将客户端IP进行Hash
int hashCode = clientIp.hashCode();
// 取模得到服务器下标
int index = hashCode % serverList.size();
// 返回对应下标的服务器
return serverList.get(index);
}
// 服务器类
public static class Server {
private String ip;
private int port;
public Server(String ip, int port) {
this.ip = ip;
this.port = port;
}
// getter和setter方法省略
}
}
还有响应时间负载、异常率负载等这种动态指标的就不演示了,因为需要接入指标,大体负载逻辑都大同小异