• 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]落在哪个区间。

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

    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  
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

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

    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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果如下:

    service A query count: 60, service B query count 40

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

  • 相关阅读:
    【uni-app】小程序往缓存里添加对象并读取数据
    怎么把两个pdf合并成一个pdf?
    【华为OD机试真题 JAVA】火锅
    利用软raid程序来配置实现“RAID1+0”阵列
    Spring的事务传遍机制是什么?
    golang和mysql中的数据类型的对应
    python in excel 如何尝鲜 有手就行
    学生个人单页面网页作业 学生网页设计成品 静态HTML网页单页制作 dreamweaver网页设计与制作代码 web前端期末大作业
    前端基础之《Bootstrap(9)—CSS组件_分页、标签、警告框和进度条》
    Maven了解&运用配置
  • 原文地址:https://blog.csdn.net/m0_73728511/article/details/133683824