go-zero 如何扛住流量衝擊(二)
本篇文章承接上一篇go-zero 如何扛住流量衝擊(一)。
上一篇介紹的是 go-zero
中滑動視窗限流,本篇介紹另外一個 tokenlimit
,令牌桶限流。
使用
const (
burst = 100
rate = 100
seconds = 5
)
store := redis.NewRedis("localhost:6379", "node", "")
fmt.Println(store.Ping())
// New tokenLimiter
limiter := limit.NewTokenLimiter(rate, burst, store, "rate-test")
timer := time.NewTimer(time.Second * seconds)
quit := make(chan struct{})
defer timer.Stop()
go func() {
<-timer.C
close(quit)
}()
var allowed, denied int32
var wait sync.WaitGroup
for i := 0; i < runtime.NumCPU(); i++ {
wait.Add(1)
go func() {
for {
select {
case <-quit:
wait.Done()
return
default:
if limiter.Allow() {
atomic.AddInt32(&allowed, 1)
} else {
atomic.AddInt32(&denied, 1)
}
}
}
}()
}
wait.Wait()
fmt.Printf("allowed: %d, denied: %d, qps: %d\n", allowed, denied, (allowed+denied)/seconds)
tokenlimit
從整體上令牌桶生產 token 邏輯如下:
- 使用者配置的平均傳送速率為 r,則每隔 1/r 秒一個令牌被加入到桶中;
- 假設桶中最多可以存放 b 個令牌。如果令牌到達時令牌桶已經滿了,那麼這個令牌會被丟棄;
- 當流量以速率 v 進入,從桶中以速率 v 取令牌,拿到令牌的流量通過,拿不到令牌流量不通過,執行熔斷邏輯;
go-zero
在兩類限流器下都採取 lua script
的方式,依賴 redis 可以做到分散式限流,lua script
同時可以做到對 token 生產讀取操作的原子性。
下面來看看 lua script
控制的幾個關鍵屬性:
argument | mean |
---|---|
ARGV[1] | rate「每秒生成幾個令牌」 |
ARGV[2] | burst「令牌桶最大值」 |
ARGV[3] | now_time「當前時間戳」 |
ARGV[4] | get token nums「開發者需要獲取的 token 數」 |
KEYS[1] | 表示資源的 tokenkey |
KEYS[2] | 表示重新整理時間的 key |
-- 返回是否可以活獲得預期的token
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
-- fill_time:需要填滿 token_bucket 需要多久
local fill_time = capacity/rate
-- 將填充時間向下取整
local ttl = math.floor(fill_time*2)
-- 獲取目前 token_bucket 中剩餘 token 數
-- 如果是第一次進入,則設定 token_bucket 數量為 令牌桶最大值
local last_tokens = tonumber(redis.call("get", KEYS[1]))
if last_tokens == nil then
last_tokens = capacity
end
-- 上一次更新 token_bucket 的時間
local last_refreshed = tonumber(redis.call("get", KEYS[2]))
if last_refreshed == nil then
last_refreshed = 0
end
local delta = math.max(0, now-last_refreshed)
-- 通過當前時間與上一次更新時間的跨度,以及生產token的速率,計算出新的token數
-- 如果超過 max_burst,多餘生產的token會被丟棄
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
if allowed then
new_tokens = filled_tokens - requested
end
-- 更新新的token數,以及更新時間
redis.call("setex", KEYS[1], ttl, new_tokens)
redis.call("setex", KEYS[2], ttl, now)
return allowed
上述可以看出 lua script
:只涉及對 token 操作,保證 token 生產合理和讀取合理。
函式分析
從上述流程中看出:
- 有多重保障機制,保證限流一定會完成。
- 如果
redis limiter
失效,至少在程式內rate limiter
兜底。 - 重試
redis limiter
機制保證儘可能地正常執行。
總結
go-zero
中的 tokenlimit
限流方案適用於瞬時流量衝擊,現實請求場景並不以恆定的速率。令牌桶相當預請求,當真實的請求到達不至於瞬間被打垮。當流量衝擊到一定程度,則才會按照預定速率進行消費。
但是生產token
上,不能按照當時的流量情況作出動態調整,不夠靈活,還可以進行進一步優化。此外可以參考Token bucket WIKI中提到分層令牌桶,根據不同的流量頻寬,分至不同排隊中。
參考
如果覺得文章不錯,歡迎 github 點個 star ? 。同時歡迎大家使用 go-zero
,https://github.com/tal-tech/go-zero
更多原創文章乾貨分享,請關注公眾號
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- go-zero 如何扛住流量衝擊(一)Go
- 如何扛住遊戲流量高峰?Evil Dead 主創這樣說遊戲
- 特殊時期,釘釘如何透過單元化扛住流量高峰?
- 扛住阿里雙十一高併發流量,Sentinel是怎麼做到的?阿里
- “雙十一、二” 業務高峰如何扛住?韻達快遞選擇 TDengine
- 千萬級流量衝擊下,如何保證極致效能
- OceanBase X 成都有惠 | 扛住資料流量洪峰,找對 CP 是關鍵!
- CIO要頂住詞彙爆炸的衝擊(轉)
- 從步履蹣跚到舉重若輕,阿里基礎架構如何扛住全球最猛的流量洪峰?阿里架構
- 面試官:十問泛型,你能扛住嗎?面試泛型
- 淺談時序資料庫核心:如何用單機扛住億級資料寫入資料庫
- 阿里P8面試官:如何設計一個扛住千萬級併發的架構?阿里面試架構
- 如何應對AI帶來的衝擊AI
- 網站被劫持攻擊以及流量攻擊如何解決網站
- 如何防禦惡意流量攻擊(CC、DDoS)?
- go-zero之支付服務二Go
- 新氧二季度收入3.28億元,網際網路巨頭衝擊下流量販賣模式難以為繼模式
- GitHub 標星 11000+,阿里開源的微服務元件如何連續 10 年扛住雙十一大促?Github阿里微服務元件
- 大促活動如何抵禦大流量 DDoS 攻擊?
- CDN流量放大攻擊思路
- 阿里P8面試官:如何設計一個扛住千萬級併發的架構(超級詳細)-續阿里面試架構
- 緩衝區溢位攻擊
- 為什麼阿里巴巴這一次能扛得起 11 億的流量?阿里
- 貝泰妮衝擊IPO:功能性護膚品生意如何?
- 全球晶片供應面臨衝擊晶片
- Go 對 Python 產生的衝擊GoPython
- go-zero 微服務實戰系列(二、服務拆分)Go微服務
- 5種限流演算法,7種限流方式,擋住突發流量?演算法
- App 被刷影片流量如何防禦,DDoS攻擊一招解決APP
- 流量劫持攻擊之鏈路劫持剖析
- Atom在Vscode衝擊下被淘汰 - githubVSCodeGithub
- 飽受衝擊,依舊迎風翱翔
- 居然之家擬衝擊千億市值,能行嗎?
- 牙博士衝擊IPO:口腔生意真不錯
- 企業專案遷移go-zero全攻略(二)Go
- 前端安全系列之二:如何防止CSRF攻擊?前端
- ASLR 是如何保護 Linux 系統免受緩衝區溢位攻擊的Linux
- 牛津大學:新冠疫情衝擊美國人均壽命創二戰以來最大降幅