Golang优雅实现按比例切分流量

我们在进行灰度发布时,往往需要转发一部分流量到新上线的服务上,进行小规模的验证,随着功能的不断完善,我们也会逐渐增加转发的流量,这就需要按比例去切分流量,那么如何实现流量切分呢?

我们很容易想到通过生成随机数方式进行实现,通过判断生成随机数是否落在指定区间内,从而决定是否进行流量的转发,这种方式虽然实现很简单,但是它有两点弊端:

  • 每次都要生成新的随机数,这是有性能损耗的,尤其是并发量高的场景下更为明显;
  • 随机数的生成往往不够均匀,比如有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]落在哪个区间。

看文字可能觉得理解起来有些别扭,这里贴上完整代码:

python 复制代码
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  
    }
}

接下来我们检测下这段代码是否真的能精准切分流量:

python 复制代码
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

其实思路很简单:通过请求数与基数取余,确保在一定范围内总能按比例 实现流量切分;通过打乱数组确保流量分布尽可能均匀

相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
mghio5 小时前
Dubbo 中的集群容错
java·微服务·dubbo
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github