负载均衡之加权轮询调度算法
前言
WRR(Weighted Round Robin)基于权重轮询调度算法。
过去只是在nginx上直接使用,由于最近在自行开发一个webserver,涉及到一个负载均衡功能,因此自行实现基于权重轮询调度算法。
分析
假设有三台服务器(IP 端口 权重)分别为如下:
- 服务器A:10.1.1.92:8081 weight:5
- 服务器B:10.1.1.93:8081 weight: 1
- 服务器C:10.1.1.94:8081 weight: 1
当实现加权轮询调度,可能第一想到的方法是先请求5次服务器A,后分别各请求1次服务器B及服务器C。这种方法存在一个弊端,服务器A在某一段时间内被集中调度。
像nginx负载均衡实现基于权重轮询调度,会比较均匀的选择服务器,而不是简单的权重分配。Nginx基于权重的轮询算法的实现:Upstream: smooth weighted round-robin balancing,对比它还实现平滑轮询算法。
同样是上面三台服务器,负载均衡选择顺序会变成如下:
A A B A C A A
相当于简单的基于权重的轮询算法,三台服务器之间负载更加均衡。
实现
大致过程如下:
- 累加计算权重总值
- 计算当前节点权重,公式:当前节点权重 = 当前节点权重 + 初始权重
- 选择权重最大的节点作为本次负载节点,并且权重最大的节点减去总权重
A B C
0 0 0 (初始化权重)
5 1 1 (服务器A被选中)
-2 1 1 (由于服务器A权重在此次最大,所以减去总权重7)
3 2 2 (服务器A被选中)
-4 2 2 (由于服务器A权重在此次最大,所以减去总权重7)
1 3 3 (服务器B被选中)
1 -4 3
6 -3 4 (服务器A被选中)
-1 -3 4
4 -2 5 (服务器C被选中)
4 -2 -2
9 -1 -1 (服务器A被选中)
2 -1 -1
7 0 0 (服务器A被选中)
0 0 0
代码实现
# 创建一个Server类,当然使用struct也可以。
class Server {
private:
// 负载均衡IP
std::string ip;
// 负载均衡端口
unsigned int port;
// 权重
int weight;
// 当前权重
int curWeight;
public:
Server(std::string ip_, unsigned int port_, int weight_)
: ip(std::move(ip_)),
port(port_),
weight(weight_),
curWeight(0) {
}
~Server() {
ip = "";
port = 0;
weight = 0;
curWeight = 0;
}
std::string getIp() {
return ip;
}
int getPort() const {
return port;
}
void setWeight(int weight_) {
weight = weight_;
}
int getWeight() const {
return weight;
}
void setCurWeight(int weight_) {
curWeight = weight_;
}
int getCurWeight() const {
return curWeight;
}
};
# 负载均衡服务器数组(也即对应上述样例子中的A、B、C)
std::vector<Server> serverLists;
# 负载均衡选择方法
int totalWeight = 0; // 总权重
int maxWeight = 0; // 当前最大权重
int index = -1; // 最大权重的index
for (int i = 0; i < serverLists.size(); ++i) {
// 计算总权重
totalWeight += serverLists[i].getWeight();
// 计算当前权重 公式:当前节点权重 = 当前节点权重 + 初始权重
serverLists[i].setCurWeight(serverLists[i].getWeight() + serverLists[i].getCurWeight());
// 选取最大权重的节点
if (serverLists[i].getCurWeight() > maxWeight) {
maxWeight = serverLists[i].getCurWeight();
index = i;
}
}
// 将被选择的节点权重(权重最大节点) 减 总权重, 用于下一轮选择
serverLists[index].setCurWeight(serverLists[index].getCurWeight() - totalWeight);
// 返回被选择的节点
return serverLists[index];
参考文章:
平滑的基于权重的轮询算法