日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

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

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

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

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

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

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

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

工作原理

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

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

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

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

源碼實現

源碼分析我們還是以 go-zero 項目為例,首先來看生成令牌的部分,依然是使用 Redis 來實現。CU028資訊網——每日最新資訊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 數量和刷新時間。CU028資訊網——每日最新資訊28at.com

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

限流器初始化:CU028資訊網——每日最新資訊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 發生故障了,那么就使用這個,算是一個保障,盡量避免系統被突發流量拖垮。CU028資訊網——每日最新資訊28at.com

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

提供了四個可調用方法:CU028資訊網——每日最新資訊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 方法:CU028資訊網——每日最新資訊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}

最后看一下進程內限流的啟動與恢復:CU028資訊網——每日最新資訊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        }    }}

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

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

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

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

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

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

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
欧美美女操人视频| 国产精品久久久久影院亚瑟| 亚洲欧美日韩另类| 欧美电影资源| 欧美成人午夜激情在线| 欧美人与性动交cc0o| 国产精品xxxxx| 国产一区欧美日韩| 亚洲人午夜精品免费| 国产亚洲人成网站在线观看| 影音先锋国产精品| 新67194成人永久网站| 亚洲精品视频一区二区三区| 黄色亚洲免费| 99国产精品自拍| 久久国产精品色婷婷| 欧美激情一区二区三区全黄| 欧美丝袜第一区| 一区二区亚洲精品| 在线午夜精品| 乱中年女人伦av一区二区| 欧美日韩无遮挡| 精品成人在线观看| 亚洲综合国产精品| 欧美激情亚洲国产| 国产一区二区三区高清| 99这里只有精品| 久久人人爽人人爽爽久久| 国产精品xxxxx| 在线成人h网| 性色av一区二区三区| 欧美日本在线看| 在线观看av一区| 午夜亚洲性色福利视频| 欧美日韩高清不卡| 在线观看不卡av| 欧美在线影院在线视频| 国产精品ⅴa在线观看h| 亚洲人体大胆视频| 久久久在线视频| 国产精品一区二区三区观看| 亚洲精品日韩在线观看| 久久亚洲综合色| 国产午夜精品美女视频明星a级| 一区二区电影免费观看| 欧美 日韩 国产在线| 国产一区二区三区奇米久涩| 亚洲五月六月| 欧美人与禽性xxxxx杂性| 亚洲国产裸拍裸体视频在线观看乱了 | 午夜精品久久久久久久久久久久| 欧美日韩国产综合视频在线| 亚洲福利电影| 久久尤物视频| 国户精品久久久久久久久久久不卡| 亚洲一区激情| 欧美三日本三级少妇三2023| 亚洲品质自拍| 欧美va天堂| 精品成人乱色一区二区| 欧美中在线观看| 国产精品毛片大码女人| 一区二区三区精密机械公司| 欧美另类人妖| 樱花yy私人影院亚洲| 欧美中文字幕视频在线观看| 国产精品丝袜白浆摸在线| 一区二区三区国产在线| 欧美日韩精品一区二区三区| 亚洲精品一区二区三区四区高清| 能在线观看的日韩av| 精品999日本| 久久男人资源视频| 激情综合色综合久久综合| 久久久久久久激情视频| 欧美国产日韩一区二区| 亚洲第一视频| 久久综合九色九九| 激情欧美一区二区三区| 久久天堂精品| 亚洲成人原创| 免费在线欧美视频| 亚洲精品乱码久久久久久| 欧美护士18xxxxhd| 一区二区精品| 国产精品久久久久久av福利软件| 亚洲专区一区二区三区| 国产欧美精品| 久久久久久九九九九| 在线观看日韩www视频免费| 蜜桃av噜噜一区| 亚洲欧洲一级| 欧美日韩视频免费播放| 亚洲一区国产精品| 国产日本亚洲高清| 久久亚洲捆绑美女| 亚洲人成在线免费观看| 欧美日韩精品免费观看视频| 亚洲香蕉成视频在线观看| 国产精品一区免费观看| 久久久久久久国产| 亚洲国产另类 国产精品国产免费| 欧美国产亚洲精品久久久8v| 中文国产亚洲喷潮| 国产日韩成人精品| 老鸭窝毛片一区二区三区| 亚洲日本中文| 国产精品久久久| 久久精品国产精品亚洲| 亚洲高清电影| 欧美日韩亚洲精品内裤| 午夜影院日韩| 亚洲福利视频一区| 欧美视频第二页| 久久精品国产清高在天天线| 亚洲风情亚aⅴ在线发布| 欧美日韩精品免费 | 欧美激情一区二区三区蜜桃视频 | 欧美中文字幕视频在线观看| 在线电影国产精品| 欧美午夜不卡视频| 久久精品国产96久久久香蕉| 亚洲精品在线一区二区| 国产美女精品在线| 欧美/亚洲一区| 亚洲男人影院| 亚洲国产成人在线播放| 国产精品国色综合久久| 久久精品女人| aa成人免费视频| 国产综合激情| 一区二区三区**美女毛片| 国产亚洲aⅴaaaaaa毛片| 欧美成人免费观看| 午夜精品电影| 亚洲激情小视频| 国产欧美短视频| 欧美日韩1区2区| 亚洲日本理论电影| 国产精品免费观看在线| 欧美成年人视频| 欧美一进一出视频| 日韩一二三在线视频播| 国内精品久久久久伊人av| 欧美日韩综合另类| 久久综合狠狠综合久久综青草 | 嫩模写真一区二区三区三州| 欧美一级片久久久久久久| 日韩视频不卡| 在线观看日韩www视频免费| 国产精品乱码人人做人人爱| 欧美电影免费网站| 久久国产精品久久久久久久久久| 亚洲天堂av在线免费| 亚洲国产精品悠悠久久琪琪 | 亚洲第一黄网| 国产综合色产| 国产精品美女久久久| 欧美激情中文字幕乱码免费| 久久精品国产77777蜜臀| 亚洲女人天堂成人av在线| 亚洲美女黄色| 亚洲高清在线观看一区| 国产一区二区三区在线播放免费观看| 欧美日韩一区综合| 欧美成人一区二区在线 | 欧美视频在线视频| 欧美激情影院| 欧美18av| 久久午夜国产精品| 久久都是精品| 香蕉av福利精品导航| 亚洲一区二区三区四区五区黄| 亚洲精品一区二区三区婷婷月 | 久久综合色播五月| 欧美影院精品一区| 亚洲欧美日本精品| 亚洲一卡久久| 宅男噜噜噜66一区二区 | 农村妇女精品| 久久综合中文| 久久午夜电影| 麻豆成人在线观看| 久久综合给合久久狠狠狠97色69| 欧美中文字幕第一页| 午夜久久久久久| 午夜精品成人在线| 午夜精品一区二区在线观看| 亚洲一区免费网站| 激情久久五月| 国产综合视频在线观看| 国产亚洲激情在线| 国产亚洲欧美激情| 国产一区二区毛片| 好看的日韩视频| 国内精品伊人久久久久av一坑| 国产色产综合色产在线视频 | 一区二区三区视频在线播放| 99热精品在线| 亚洲视频专区在线| 亚洲一区二区三区在线| 亚洲欧美日韩在线观看a三区| 亚洲综合精品| 欧美一区国产一区|