Golang實(shí)現(xiàn)按比例切分流量的示例詳解
我們?cè)谶M(jìn)行灰度發(fā)布時(shí),往往需要轉(zhuǎn)發(fā)一部分流量到新上線(xiàn)的服務(wù)上,進(jìn)行小規(guī)模的驗(yàn)證,隨著功能的不斷完善,我們也會(huì)逐漸增加轉(zhuǎn)發(fā)的流量,這就需要按比例去切分流量,那么如何實(shí)現(xiàn)流量切分呢?
我們很容易想到通過(guò)生成隨機(jī)數(shù)方式進(jìn)行實(shí)現(xiàn),通過(guò)判斷生成隨機(jī)數(shù)是否落在指定區(qū)間內(nèi),從而決定是否進(jìn)行流量的轉(zhuǎn)發(fā),這種方式雖然實(shí)現(xiàn)很簡(jiǎn)單,但是它有兩點(diǎn)弊端:
- 每次都要生成新的隨機(jī)數(shù),這是有性能損耗的,尤其是并發(fā)量高的場(chǎng)景下更為明顯;
- 隨機(jī)數(shù)的生成往往不夠均勻,比如有A、B兩個(gè)服務(wù),流量比例3:7,如果使用隨機(jī)數(shù)方式,如果運(yùn)氣不好的話(huà)有可能請(qǐng)求100次全落在B服務(wù)上。
那有沒(méi)有性能開(kāi)銷(xiāo)又小,又能精準(zhǔn)切分流量的方式呢?當(dāng)然是有的。實(shí)現(xiàn)思路如下:
- 確定比例,并根據(jù)比例得到一個(gè)基數(shù)base,例如比例是3:7,那么基數(shù)就是10;
- 生成長(zhǎng)度為基數(shù)base的數(shù)組source,并填充數(shù)據(jù)0、1、2、3、4、5...;
- 打亂數(shù)組source中元素順序;
- 創(chuàng)建全局計(jì)數(shù)器queryCount,每次有請(qǐng)求時(shí)加1(確保原子性);
- 計(jì)算計(jì)數(shù)器queryCount與base取余后的值rate,并得到數(shù)組中對(duì)應(yīng)位置的值source[rate];
- 判斷source[rate]落在哪個(gè)區(qū)間。
看文字可能覺(jué)得理解起來(lái)有些別扭,這里貼上完整代碼:
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
}
}接下來(lái)我們檢測(cè)下這段代碼是否真的能精準(zhǔn)切分流量:
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)
}執(zhí)行結(jié)果如下:
service A query count: 60, service B query count 40
其實(shí)思路很簡(jiǎn)單:通過(guò)請(qǐng)求數(shù)與基數(shù)取余,確保在一定范圍內(nèi)總能按比例 實(shí)現(xiàn)流量切分;通過(guò)打亂數(shù)組確保流量分布盡可能均勻。當(dāng)然流量切分還有其他實(shí)現(xiàn)方式,如果大家有更精妙的實(shí)現(xiàn),歡迎評(píng)論區(qū)留言哈。
到此這篇關(guān)于Golang實(shí)現(xiàn)按比例切分流量的示例詳解的文章就介紹到這了,更多相關(guān)Golang按比例切分流量?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言中函數(shù)可變參數(shù)(Variadic Parameter)詳解
在Python中,在函數(shù)參數(shù)不確定數(shù)量的情況下,可以動(dòng)態(tài)在函數(shù)內(nèi)獲取參數(shù)。在Go語(yǔ)言中,也有類(lèi)似的實(shí)現(xiàn)方式,本文就來(lái)為大家詳細(xì)講解一下2022-07-07
Golang利用Template模板動(dòng)態(tài)生成文本
Go語(yǔ)言中的Go?Template是一種用于生成文本輸出的簡(jiǎn)單而強(qiáng)大的模板引擎,它提供了一種靈活的方式來(lái)生成各種格式的文本,下面我們就來(lái)看看具體如何使用Template實(shí)現(xiàn)動(dòng)態(tài)文本生成吧2023-09-09
利用go語(yǔ)言實(shí)現(xiàn)Git?重命名遠(yuǎn)程分支??
這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)Git?重命名遠(yuǎn)程分支,文章基于go語(yǔ)言的基礎(chǔ)展開(kāi)Git?重命名遠(yuǎn)程分支的實(shí)現(xiàn)過(guò)程,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助2022-06-06
Go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之二叉樹(shù)必會(huì)知識(shí)點(diǎn)總結(jié)
如果你是一個(gè)開(kāi)發(fā)人員,或多或少對(duì)樹(shù)型結(jié)構(gòu)都有一定的認(rèn)識(shí)。二叉樹(shù)作為樹(shù)的一種,是一種重要的數(shù)據(jù)結(jié)構(gòu),也是面試官經(jīng)??嫉臇|西。本文為大家總結(jié)了一些二叉樹(shù)必會(huì)知識(shí)點(diǎn),需要的可以參考一下2022-08-08

