Golang實(shí)現(xiàn)按比例切分流量的示例詳解
我們?cè)谶M(jìn)行灰度發(fā)布時(shí),往往需要轉(zhuǎn)發(fā)一部分流量到新上線的服務(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)氣不好的話有可能請(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 Gin框架中的binding驗(yàn)證器使用小結(jié)
Gin框架中的binding驗(yàn)證器為我們提供了簡(jiǎn)便的數(shù)據(jù)綁定和驗(yàn)證功能,通過(guò)合理使用binding和validate標(biāo)簽,我們可以確保API接口的數(shù)據(jù)合法性和完整性,這篇文章主要介紹了Go Gin框架中的binding驗(yàn)證器使用指南,需要的朋友可以參考下2024-07-07
Golang設(shè)計(jì)模式之外觀模式講解和代碼示例
外觀是一種結(jié)構(gòu)型設(shè)計(jì)模式, 能為復(fù)雜系統(tǒng)、 程序庫(kù)或框架提供一個(gè)簡(jiǎn)單 (但有限) 的接口,這篇文章就給大家詳細(xì)介紹一下Golang的外觀模式,文中有詳細(xì)的代碼示例,具有一定的參考價(jià)值,需要的朋友可以參考下2023-06-06
GoLang strings.Builder底層實(shí)現(xiàn)方法詳解
自從學(xué)習(xí)go一個(gè)月以來(lái),我多少使用了一下strings.Builder,略有心得。你也許知道它,特別是你了解bytes.Buffer的話。所以我在此分享一下我的心得,并希望能對(duì)你有所幫助2022-10-10
go類型轉(zhuǎn)換及與C的類型轉(zhuǎn)換方式
這篇文章主要介紹了go類型轉(zhuǎn)換及與C的類型轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
go mod 安裝依賴 unkown revision問(wèn)題的解決方案
這篇文章主要介紹了go mod 安裝依賴 unkown revision問(wèn)題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
Ubuntu安裝Go語(yǔ)言運(yùn)行環(huán)境
由于最近偏愛(ài)Ubuntu,在加上作為一門(mén)開(kāi)源語(yǔ)言,在Linux上從源代碼開(kāi)始搭建環(huán)境更讓人覺(jué)得有趣味性。讓我們直接先從Go語(yǔ)言的環(huán)境搭建開(kāi)始2015-04-04

