国产精品嫩草99av在线_一区在线视频观看_欧美高清一区_欧美 日韩 国产 一区_99精品欧美一区二区三区_久久大香伊蕉在人线观看热2_一色屋精品视频在线观看网站_在线亚洲国产精品网站_亚洲区一区二区三区_你懂的视频一区二区

當前位置:首頁 > 科技  > 軟件

Go-Zero 是如何實現令牌桶限流的?

來源: 責編: 時間:2023-08-14 22:01:53 3339觀看
導讀上一篇文章介紹了 如何實現計數器限流。主要有兩種實現方式,分別是固定窗口和滑動窗口,并且分析了 go-zero 采用固定窗口方式實現的源碼。但是采用固定窗口實現的限流器會有兩個問題:會出現請求量超出限制值兩倍的情況無

上一篇文章介紹了 如何實現計數器限流。主要有兩種實現方式,分別是固定窗口和滑動窗口,并且分析了 go-zero 采用固定窗口方式實現的源碼。i4H28資訊網——每日最新資訊28at.com

但是采用固定窗口實現的限流器會有兩個問題:i4H28資訊網——每日最新資訊28at.com

  1. 會出現請求量超出限制值兩倍的情況
  2. 無法很好處理流量突增問題

這篇文章來介紹一下令牌桶算法,可以很好解決以上兩個問題。i4H28資訊網——每日最新資訊28at.com

工作原理

算法概念如下:i4H28資訊網——每日最新資訊28at.com

  • 令牌以固定速率生成;
  • 生成的令牌放入令牌桶中存放,如果令牌桶滿了則多余的令牌會直接丟棄,當請求到達時,會嘗試從令牌桶中取令牌,取到了令牌的請求可以執行;
  • 如果桶空了,那么嘗試取令牌的請求會被直接丟棄。

圖片圖片i4H28資訊網——每日最新資訊28at.com

令牌桶算法既能夠將所有的請求平均分布到時間區間內,又能接受服務器能夠承受范圍內的突發請求,因此是目前使用較為廣泛的一種限流算法。i4H28資訊網——每日最新資訊28at.com

源碼實現

源碼分析我們還是以 go-zero 項目為例,首先來看生成令牌的部分,依然是使用 Redis 來實現。i4H28資訊網——每日最新資訊28at.com

// core/limit/tokenlimit.go// 生成 token 速率script = `local rate = tonumber(ARGV[1])// 通容量local capacity = tonumber(ARGV[2])// 當前時間戳local now = tonumber(ARGV[3])// 請求數量local requested = tonumber(ARGV[4])// 需要多少秒才能把桶填滿local fill_time = capacity/rate// 向下取整,ttl 為填滿時間 2 倍local ttl = math.floor(fill_time*2)// 當前桶剩余容量,如果為 nil,說明第一次使用,賦值為桶最大容量local last_tokens = tonumber(redis.call("get", KEYS[1]))if last_tokens == nil then    last_tokens = capacityend// 上次請求時間戳,如果為 nil 則賦值 0local last_refreshed = tonumber(redis.call("get", KEYS[2]))if last_refreshed == nil then    last_refreshed = 0end// 距離上一次請求的時間跨度local delta = math.max(0, now-last_refreshed)// 距離上一次請求的時間跨度能生成的 token 數量和桶內剩余 token 數量的和// 與桶容量比較,取二者的小值local filled_tokens = math.min(capacity, last_tokens+(delta*rate))// 判斷請求數量和桶內 token 數量的大小local allowed = filled_tokens >= requested// 被請求消耗掉之后,更新剩余 token 數量local new_tokens = filled_tokensif allowed then    new_tokens = filled_tokens - requestedend// 更新 redis tokenredis.call("setex", KEYS[1], ttl, new_tokens)// 更新 redis 刷新時間redis.call("setex", KEYS[2], ttl, now)return allowed`

Redis 中主要保存兩個 key,分別是 token 數量和刷新時間。i4H28資訊網——每日最新資訊28at.com

核心思想就是比較兩次請求時間間隔內生成的 token 數量 + 桶內剩余 token 數量,和請求量之間的大小,如果滿足則允許,否則則不允許。i4H28資訊網——每日最新資訊28at.com

限流器初始化:i4H28資訊網——每日最新資訊28at.com

// A TokenLimiter controls how frequently events are allowed to happen with in one second.type TokenLimiter struct {    // 生成 token 速率    rate           int    // 桶容量    burst          int    store          *redis.Redis    // 桶 key    tokenKey       string    // 桶刷新時間 key    timestampKey   string    rescueLock     sync.Mutex    // redis 健康標識    redisAlive     uint32    // redis 健康監控啟動狀態    monitorStarted bool    // 內置單機限流器    rescueLimiter  *xrate.Limiter}// NewTokenLimiter returns a new TokenLimiter that allows events up to rate and permits// bursts of at most burst tokens.func NewTokenLimiter(rate, burst int, store *redis.Redis, key string) *TokenLimiter {    tokenKey := fmt.Sprintf(tokenFormat, key)    timestampKey := fmt.Sprintf(timestampFormat, key)    return &TokenLimiter{        rate:          rate,        burst:         burst,        store:         store,        tokenKey:      tokenKey,        timestampKey:  timestampKey,        redisAlive:    1,        rescueLimiter: xrate.NewLimiter(xrate.Every(time.Second/time.Duration(rate)), burst),    }}

其中有一個變量 rescueLimiter,這是一個進程內的限流器。如果 Redis 發生故障了,那么就使用這個,算是一個保障,盡量避免系統被突發流量拖垮。i4H28資訊網——每日最新資訊28at.com

圖片圖片i4H28資訊網——每日最新資訊28at.com

提供了四個可調用方法:i4H28資訊網——每日最新資訊28at.com

// Allow is shorthand for AllowN(time.Now(), 1).func (lim *TokenLimiter) Allow() bool {    return lim.AllowN(time.Now(), 1)}// AllowCtx is shorthand for AllowNCtx(ctx,time.Now(), 1) with incoming context.func (lim *TokenLimiter) AllowCtx(ctx context.Context) bool {    return lim.AllowNCtx(ctx, time.Now(), 1)}// AllowN reports whether n events may happen at time now.// Use this method if you intend to drop / skip events that exceed the rate.// Otherwise, use Reserve or Wait.func (lim *TokenLimiter) AllowN(now time.Time, n int) bool {    return lim.reserveN(context.Background(), now, n)}// AllowNCtx reports whether n events may happen at time now with incoming context.// Use this method if you intend to drop / skip events that exceed the rate.// Otherwise, use Reserve or Wait.func (lim *TokenLimiter) AllowNCtx(ctx context.Context, now time.Time, n int) bool {    return lim.reserveN(ctx, now, n)}

最終調用的都是 reverveN 方法:i4H28資訊網——每日最新資訊28at.com

func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) bool {    // 判斷 Redis 健康狀態,如果 Redis 故障,則使用進程內限流器    if atomic.LoadUint32(&lim.redisAlive) == 0 {        return lim.rescueLimiter.AllowN(now, n)    }    // 執行限流腳本    resp, err := lim.store.EvalCtx(ctx,        script,        []string{            lim.tokenKey,            lim.timestampKey,        },        []string{            strconv.Itoa(lim.rate),            strconv.Itoa(lim.burst),            strconv.FormatInt(now.Unix(), 10),            strconv.Itoa(n),        })    // redis allowed == false    // Lua boolean false -> r Nil bulk reply    if err == redis.Nil {        return false    }    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {        logx.Errorf("fail to use rate limiter: %s", err)        return false    }    if err != nil {        logx.Errorf("fail to use rate limiter: %s, use in-process limiter for rescue", err)        // 如果有異常的話,會啟動進程內限流        lim.startMonitor()        return lim.rescueLimiter.AllowN(now, n)    }    code, ok := resp.(int64)    if !ok {        logx.Errorf("fail to eval redis script: %v, use in-process limiter for rescue", resp)        lim.startMonitor()        return lim.rescueLimiter.AllowN(now, n)    }    // redis allowed == true    // Lua boolean true -> r integer reply with value of 1    return code == 1}

最后看一下進程內限流的啟動與恢復:i4H28資訊網——每日最新資訊28at.com

func (lim *TokenLimiter) startMonitor() {    lim.rescueLock.Lock()    defer lim.rescueLock.Unlock()    // 需要加鎖保護,如果程序已經啟動了,直接返回,不要重復啟動    if lim.monitorStarted {        return    }    lim.monitorStarted = true    atomic.StoreUint32(&lim.redisAlive, 0)    go lim.waitForRedis()}func (lim *TokenLimiter) waitForRedis() {    ticker := time.NewTicker(pingInterval)    // 更新監控進程的狀態    defer func() {        ticker.Stop()        lim.rescueLock.Lock()        lim.monitorStarted = false        lim.rescueLock.Unlock()    }()    for range ticker.C {        // 對 redis 進行健康監測,如果 redis 服務恢復了        // 則更新 redisAlive 標識,并退出 goroutine        if lim.store.Ping() {            atomic.StoreUint32(&lim.redisAlive, 1)            return        }    }}

參考文章:i4H28資訊網——每日最新資訊28at.com

  • https://juejin.cn/post/7052171117116522504
  • https://www.infoq.cn/article/Qg2tX8fyw5Vt-f3HH673

本文鏈接:http://www.rrqrq.com/showinfo-26-5770-0.htmlGo-Zero 是如何實現令牌桶限流的?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 「Go面經」算法 并發模型 緩存落盤 etcd actor模型

下一篇: 阿里云還會繼續降價嗎?

標簽:
  • 熱門焦點
Top 国产精品嫩草99av在线_一区在线视频观看_欧美高清一区_欧美 日韩 国产 一区_99精品欧美一区二区三区_久久大香伊蕉在人线观看热2_一色屋精品视频在线观看网站_在线亚洲国产精品网站_亚洲区一区二区三区_你懂的视频一区二区
色噜噜狠狠成人中文综合 | 一区二区三区四区av| 成人性生交大片免费看中文| 在线一区二区三区四区| 亚洲国产一区二区在线播放| 亚洲精品欧美| 久久精品一区二区三区av| 国产一区三区三区| 欧美性大战久久久久久久| 麻豆精品在线看| 91久久精品一区二区二区| 图片区小说区国产精品视频| 国产亚洲毛片| 亚洲自拍另类综合| 中文一区在线| 一区二区三区高清在线| 黄色另类av| 91精品欧美福利在线观看| 久久97超碰国产精品超碰| 色综合久久88色综合天天免费| 香蕉久久夜色精品国产使用方法| 国产农村妇女毛片精品久久莱园子 | 亚洲精品黄色| 亚洲一区二区在线免费看| 国产精品免费一区二区三区观看 | 欧美三级一区二区| 久久国产人妖系列| 69av一区二区三区| 成人精品鲁一区一区二区| 2019国产精品| 欧美午夜在线| 亚洲人成网站影音先锋播放| 亚洲精品在线二区| 亚洲一区二区美女| 欧美亚洲一区三区| 国产mv日韩mv欧美| 久久久久国产精品麻豆ai换脸| 欧美性色综合| 亚洲综合区在线| 在线精品亚洲一区二区不卡| 精品无人码麻豆乱码1区2区 | 日韩精品一卡二卡三卡四卡无卡| 色8久久精品久久久久久蜜| 美女视频黄 久久| 欧美一级黄色大片| 欧美久久久久| 亚洲线精品一区二区三区| 在线亚洲一区观看| 成人夜色视频网站在线观看| 欧美激情一区在线观看| 国产一区二区三区久久久久久久久| 午夜国产精品一区| 欧美日韩精品三区| av中文字幕不卡| 国产精品久久久久久久久晋中| 亚洲欧美高清| 国产一区二区三区免费看| 久久蜜桃av一区二区天堂| 在线视频观看日韩| 爽爽淫人综合网网站| 欧美日本一道本在线视频| 91麻豆免费观看| 亚洲香肠在线观看| 欧美一级欧美三级在线观看| 欧美一区二区| 亚洲老司机在线| 欧美做爰猛烈大尺度电影无法无天| 成人一区在线看| 亚洲乱码国产乱码精品精的特点 | 午夜亚洲视频| 国产精品亚洲一区二区三区妖精| 欧美国产精品久久| 久久精品国产清高在天天线| 国产精品99久久久| 成人免费在线视频观看| 玖玖精品视频| 99久久精品免费看国产 | 日韩午夜中文字幕| 激情综合中文娱乐网| 日本美女视频一区二区| www国产精品av| 久久动漫亚洲| 成人精品免费网站| 一区二区三区**美女毛片| 91精品中文字幕一区二区三区| 在线日韩av| 激情综合网最新| 亚洲视频一二三区| 91精品啪在线观看国产60岁| 亚洲欧洲综合| 国产成人aaa| 亚洲精品国久久99热| 日韩一区二区在线观看视频播放| 99成人在线| 国产成人精品1024| 亚洲国产精品麻豆| 精品对白一区国产伦| 久久一区二区三区av| 欧美成人一区二免费视频软件| 免费人成在线不卡| 国产精品久久久久久久浪潮网站| 欧美日韩综合在线| 亚洲精品一区二| 成人免费毛片a| 亚洲一区二区av在线| 精品不卡在线视频| 色婷婷av一区二区| 亚洲视频狠狠| 懂色av中文一区二区三区| 亚洲不卡一区二区三区| 日本一区二区三区国色天香| 欧美亚洲综合在线| 亚洲人体大胆视频| 成人黄页在线观看| 日本不卡在线视频| 亚洲欧美激情小说另类| 亚洲精品一区二区三区四区高清 | 欧美亚洲高清一区| 狠狠色噜噜狠狠狠狠色吗综合| 国产在线不卡一区| 亚洲高清免费在线| 国产精品萝li| 欧美zozozo| 欧美日韩电影一区| 久久精品五月婷婷| 在线视频观看日韩| 欧美aa国产视频| 国产一区二区三区四| 亚洲国产精品久久一线不卡| 国产精品日韩精品欧美在线| 制服.丝袜.亚洲.另类.中文| 久热综合在线亚洲精品| 一区免费在线| 成人免费黄色在线| 91网站最新网址| 欧美电影一区二区| 国产精品久久久久影院老司| 国产精品影视在线| 91在线视频网址| 色天天综合久久久久综合片| 欧美日本乱大交xxxxx| 国产精品久久久久久妇女6080| 国产一区二区不卡在线| 久久99精品久久久| 色综合久久久久| 日日欢夜夜爽一区| 一本色道久久综合亚洲aⅴ蜜桃 | 国产成人精品亚洲日本在线桃色 | 蜜臀av一区二区| 亚洲视频在线观看三级| 久久久久久99精品| 日韩欧美国产电影| 7777精品久久久大香线蕉| 91国偷自产一区二区开放时间| 亚洲一区二区四区| 亚洲巨乳在线| 狠久久av成人天堂| 99久久免费国产| 成年人午夜久久久| 粉嫩嫩av羞羞动漫久久久| 国产综合成人久久大片91| 久久激情五月婷婷| 另类的小说在线视频另类成人小视频在线| 亚洲国产欧美在线| 亚洲成人一区二区| 亚洲国产美国国产综合一区二区| 一区二区成人在线视频| 亚洲欧美电影一区二区| 国产精品久久夜| 国产精品不卡在线观看| 国产精品嫩草影院av蜜臀| 国产精品萝li| 自拍偷拍亚洲综合| 亚洲精品免费看| 亚洲精品一二三四区| 亚洲精品视频在线观看网站| 亚洲精品日韩一| 亚洲一区二区三区四区在线免费观看 | 国产日韩一级二级三级| 欧美精品一区二区三区在线| 日韩精品在线网站| 精品国产91久久久久久久妲己| 精品久久久久香蕉网| 久久久久久**毛片大全| 粉嫩aⅴ一区二区三区四区五区| 久久精品国产久精国产爱| 久久精品国产成人一区二区三区| 日韩有码一区二区三区| 青青草国产成人av片免费| 精品一区二区三区不卡 | 国精产品一区一区三区mba视频| 韩国欧美一区二区| 国产米奇在线777精品观看| 国产精品69毛片高清亚洲| 懂色av一区二区在线播放| 91一区二区在线| 狠狠综合久久| 亚洲在线黄色| 在线观看视频91| 日韩一区二区不卡|