Golang优雅实现按比例切分流量
2023-12-13 10:15:21
我们在进行灰度发布时,往往需要转发一部分流量到新上线的服务上,进行小规模的验证,随着功能的不断完善,我们也会逐渐增加转发的流量,这就需要按比例去切分流量,那么如何实现流量切分呢?
我们很容易想到通过生成随机数方式进行实现,通过判断生成随机数是否落在指定区间内,从而决定是否进行流量的转发,这种方式虽然实现很简单,但是它有两点弊端:
- 每次都要生成新的随机数,这是有性能损耗的,尤其是并发量高的场景下更为明显;
- 随机数的生成往往不够均匀,比如有A、B两个服务,流量比例3:7,如果使用随机数方式,如果运气不好的话有可能请求100次全落在B服务上。
那有没有性能开销又小,又能精准切分流量的方式呢?当然是有的。实现思路如下:
确定比例,并根据比例得到一个基数base,例如比例是3:7,那么基数就是10;
生成长度为基数base的数组source,并填充数据0、1、2、3、4、5…; 打乱数组source中元素顺序;
创建全局计数器queryCount,每次有请求时加1(确保原子性);
计算计数器queryCount与base取余后的值rate,并得到数组中对应位置的值source[rate];
判断source[rate]落在哪个区间。
看文字可能觉得理解起来有些别扭,这里贴上完整代码:
import ( "fmt" "math/rand" "sync/atomic")
type TrafficControl struct {
source []int
queryCount uint32
base int
ratio int
}
func NewTrafficControl(base int, ratio int) *TrafficControl {
source := make([]int, base)
for i := 0; i < base; i++ {
source[i] = i
}
rand.Shuffle(base, func(i, j int) {
source[i], source[j] = source[j], source[i]
})
return &TrafficControl{
source: source,
base: base,
ratio: ratio,
}
}
func (t *TrafficControl) Allow() bool {
rate := t.source[int(atomic.AddUint32(&t.queryCount, 1))%t.base]
if rate < t.ratio {
return true
} else {
return false
}
}
接下来我们检测下这段代码是否真的能精准切分流量:
func main() {
trafficCtl := NewTrafficControl(10, 6)
cnt := 100
serviceAQueryCnt := 0
serviceBQueryCnt := 0
for cnt > 0 {
if trafficCtl.Allow() {
serviceAQueryCnt++
} else {
serviceBQueryCnt++
}
cnt--
}
fmt.Printf("service A query count: %v, service B query count %v", serviceAQueryCnt, serviceBQueryCnt)
}
执行结果如下:
service A query count: 60, service B query count 40
其实思路很简单:通过请求数与基数取余,确保在一定范围内总能按比例 实现流量切分;通过打乱数组确保流量分布尽可能均匀
文章来源:https://blog.csdn.net/ldxxxxll/article/details/134887490
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!