1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-30 00:32:21 +00:00
advanced-go-programming-book/ch6-web/ch6-12-load-balance.md
2018-05-30 19:25:29 +08:00

80 lines
2.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 6.12. Load-Balance 负载均衡
本节将会讨论常见的 web 后端服务之间的负载均衡手段。
## 常见的负载均衡思路
如果我们不考虑均衡的话,现在有 n 个 endpoint我们完成业务流程实际上只需要从这 n 个中挑出其中的一个。有几种思路:
1. 按顺序挑: 例如上次选了第一台,那么这次就选第二台,下次第三台,如果已经到了最后一台,那么下一次从第一台开始。这种情况下我们可以把 endpoint 都存储在数组中,每次请求完成下游之后,将一个索引后移即可。在移到尽头时再移回数组开头处。
2. 随机挑一个: 每次都随机挑,真随机伪随机均可。设选择第 x 台机器,那么 x 可描述为 `rand.Intn() % n`
3. 根据某种权重,对下游 endpoints 进行排序,选择权重最大/小的那一个。
当然了,实际场景我们不可能无脑轮询或者无脑随机,如果对下游请求失败了,我们还需要某种机制来进行重试,如果纯粹的随机算法,存在一定的可能性使你在下一次仍然随机到这次的问题节点。
我们来看一个生产环境的负载均衡案例。
## 一种随机负载均衡算法
考虑到我们需要随机选取每次发送请求的 endpoint同时在遇到下游返回错误时换其它节点重试。所以我们设计一个大小和 endpoints 数组大小一致的索引数组,每次来新的请求,我们对索引数组做洗牌,然后取第一个元素作为选中的服务节点,如果请求失败,那么选择下一个节点重试,以此类推:
```go
var endpoints = []string {
"100.69.62.1:3232",
"100.69.62.32:3232",
"100.69.62.42:3232",
"100.69.62.81:3232",
"100.69.62.11:3232",
"100.69.62.113:3232",
"100.69.62.101:3232",
}
func init() {
rand.Seed(time.Now().UnixNano())
}
// 重点在这个 shuffle
func shuffle(slice []int) {
for i := 0; i < len(slice); i++ {
a := rand.Intn(len(slice))
b := rand.Intn(len(slice))
slice[a], slice[b] = slice[b], slice[a]
}
}
func request(params map[string]interface{}) error {
var indexes = []int {0,1,2,3,4,5,6}
var err error
shuffle(indexes)
maxRetryTimes := 3
idx := 0
for i := 0; i < maxRetryTimes; i++ {
err = apiRequest(params, indexes[idx])
if err == nil {
break
}
idx++
}
if err != nil {
// logging
return err
}
return nil
}
```
我们循环一遍 slice两两交换这个和我们平常打牌时常用的洗牌方法类似。看起来没有什么问题。
## 有没有什么问题?
## 修正后的负载均衡算法
## zk 集群的随机节点挑选问题