Nginx 加权轮询算法
之前面试被问过如何实现有权重的Map,当时没有做出来。后面听到朋友说Nginx权重分配算法,当时以为是通过权重Map解决的,学习后发现并不是。不能白学,记录一下。
基本概念
nginx可以指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
例如
upstream backend {
server a weight = 5;
server b weight = 3;
server c weight = 2;
}
按照配置,没有10个请求,5个转发到a,3个转发到b,2个转发到c。
加权轮询与三个变量有关
weight(权重)
约定权重,即在Nginx配置文件中为每个后端服务器指定的权重值。这个值是固定不变的。
effective_weight (有效权重)
初始值等于Weight。在服务器处理请求的过程中,如果Nginx检测到与某个服务器的通信错误,可能会降低其Effective Weight,以减少向该服务器发送请求的频率。当服务器恢复正常后,其Effective Weight会逐渐增加,直到恢复到原始的Weight值
current_weight(当前权重)
初始值为0,用于动态调整服务器的选择过程。Nginx在每次选择服务器时,会根据Current Weight的值来决定哪个服务器应该接收下一个请求
算法逻辑
加权轮询算法如下:
- 对于每个请求,遍历集群中的所有可用服务器,对于每个服务器执行 current_weight += effective_weight
- 累加所有可用服务的effective_weight,记为total,total += effective_weight
- 选中current_weight 最大的服务器,作为本次选定的服务器
- 修改本次选中的服务器的current_weight, current_weight -= total
假定服务器没有发生异常,请求10次,过程如下
请求序号 | 请求前current_weight值 | effective_weight | total | 选中节点 | 请求后current_weight值 |
---|---|---|---|---|---|
1 | {a=0,b=0,c=0} | {a=5,b=3,c=2} | 10 | a | {a=-5,b=3,c=2} |
2 | {a=-5,b=3,c=2} | {a=5,b=3,c=2} | 10 | b | {a=0,b=-4,c=4} |
3 | {a=0,b=-4,c=4} | {a=5,b=3,c=2} | 10 | c | {a=5,b=-1,c=-4} |
4 | {a=5,b=-1,c=-4} | {a=5,b=3,c=2} | 10 | a | {a=0,b=2,c=-2} |
5 | {a=0,b=2,c=-2} | {a=5,b=3,c=2} | 10 | a | {a=-5,b=5,c=0} |
6 | {a=-5,b=5,c=0} | {a=5,b=3,c=2} | 10 | b | {a=0,b=-2,c=2} |
7 | {a=0,b=-2,c=2} | {a=5,b=3,c=2} | 10 | a | {a=-5,b=1,c=4} |
8 | {a=-5,b=1,c=4} | {a=5,b=3,c=2} | 10 | c | {a=0,b=4,c=-4} |
9 | {a=0,b=4,c=-4} | {a=5,b=3,c=2} | 10 | b | {a=5,b=-3,c=-2} |
10 | {a=5,b=-3,c=-2} | {a=5,b=3,c=2} | 10 | a | {a=0,b=0,c=0} |
观察到10次调用中,a节点被选中5次,b节点被选中3次,c节点被选中2次,之后current_weight 又恢复到了初始状态0。
算法实现
package main
import "fmt"
type Weighted struct {
Key string
Weight int
CurrentWeight int
EffectiveWeight int
}
func NewWeighted(key string, weighted int) Weighted {
return Weighted{
Key: key,
Weight: weighted,
CurrentWeight: 0,
EffectiveWeight: weighted,
}
}
type WeightedList struct {
WeightedList []Weighted
}
func NewWeightedList(input map[string]int) WeightedList {
weightedList := make([]Weighted, 0, len(input))
for k, v := range input {
weightedList = append(weightedList, NewWeighted(k, v))
}
return WeightedList{
WeightedList: weightedList,
}
}
func (w WeightedList) WeightedRoundRobin() string {
total := 0
maxWeight := 0
for i := range w.WeightedList {
w.WeightedList[i].CurrentWeight += w.WeightedList[i].EffectiveWeight
total += w.WeightedList[i].EffectiveWeight
if w.WeightedList[i].CurrentWeight > w.WeightedList[maxWeight].CurrentWeight {
maxWeight = i
}
}
w.WeightedList[maxWeight].CurrentWeight -= total
return w.WeightedList[maxWeight].Key
}
func main() {
w := NewWeightedList(map[string]int{
"a": 5,
"b": 3,
"c": 2,
})
for _ = range 10 {
fmt.Println(w.WeightedRoundRobin())
}
}