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

當(dāng)前位置:首頁(yè) > 科技  > 軟件

Go-Zero 是如何實(shí)現(xiàn)令牌桶限流的?

來(lái)源: 責(zé)編: 時(shí)間:2023-08-14 22:01:53 3366觀看
導(dǎo)讀上一篇文章介紹了 如何實(shí)現(xiàn)計(jì)數(shù)器限流。主要有兩種實(shí)現(xiàn)方式,分別是固定窗口和滑動(dòng)窗口,并且分析了 go-zero 采用固定窗口方式實(shí)現(xiàn)的源碼。但是采用固定窗口實(shí)現(xiàn)的限流器會(huì)有兩個(gè)問(wèn)題:會(huì)出現(xiàn)請(qǐng)求量超出限制值兩倍的情況無(wú)

上一篇文章介紹了 如何實(shí)現(xiàn)計(jì)數(shù)器限流。主要有兩種實(shí)現(xiàn)方式,分別是固定窗口和滑動(dòng)窗口,并且分析了 go-zero 采用固定窗口方式實(shí)現(xiàn)的源碼。zZv28資訊網(wǎng)——每日最新資訊28at.com

但是采用固定窗口實(shí)現(xiàn)的限流器會(huì)有兩個(gè)問(wèn)題:zZv28資訊網(wǎng)——每日最新資訊28at.com

  1. 會(huì)出現(xiàn)請(qǐng)求量超出限制值兩倍的情況
  2. 無(wú)法很好處理流量突增問(wèn)題

這篇文章來(lái)介紹一下令牌桶算法,可以很好解決以上兩個(gè)問(wèn)題。zZv28資訊網(wǎng)——每日最新資訊28at.com

工作原理

算法概念如下:zZv28資訊網(wǎng)——每日最新資訊28at.com

  • 令牌以固定速率生成;
  • 生成的令牌放入令牌桶中存放,如果令牌桶滿了則多余的令牌會(huì)直接丟棄,當(dāng)請(qǐng)求到達(dá)時(shí),會(huì)嘗試從令牌桶中取令牌,取到了令牌的請(qǐng)求可以執(zhí)行;
  • 如果桶空了,那么嘗試取令牌的請(qǐng)求會(huì)被直接丟棄。

圖片圖片zZv28資訊網(wǎng)——每日最新資訊28at.com

令牌桶算法既能夠?qū)⑺械恼?qǐng)求平均分布到時(shí)間區(qū)間內(nèi),又能接受服務(wù)器能夠承受范圍內(nèi)的突發(fā)請(qǐng)求,因此是目前使用較為廣泛的一種限流算法。zZv28資訊網(wǎng)——每日最新資訊28at.com

源碼實(shí)現(xiàn)

源碼分析我們還是以 go-zero 項(xiàng)目為例,首先來(lái)看生成令牌的部分,依然是使用 Redis 來(lái)實(shí)現(xiàn)。zZv28資訊網(wǎng)——每日最新資訊28at.com

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

Redis 中主要保存兩個(gè) key,分別是 token 數(shù)量和刷新時(shí)間。zZv28資訊網(wǎng)——每日最新資訊28at.com

核心思想就是比較兩次請(qǐng)求時(shí)間間隔內(nèi)生成的 token 數(shù)量 + 桶內(nèi)剩余 token 數(shù)量,和請(qǐng)求量之間的大小,如果滿足則允許,否則則不允許。zZv28資訊網(wǎng)——每日最新資訊28at.com

限流器初始化:zZv28資訊網(wǎng)——每日最新資訊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    // 桶刷新時(shí)間 key    timestampKey   string    rescueLock     sync.Mutex    // redis 健康標(biāo)識(shí)    redisAlive     uint32    // redis 健康監(jiān)控啟動(dòng)狀態(tài)    monitorStarted bool    // 內(nèi)置單機(jī)限流器    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),    }}

其中有一個(gè)變量 rescueLimiter,這是一個(gè)進(jìn)程內(nèi)的限流器。如果 Redis 發(fā)生故障了,那么就使用這個(gè),算是一個(gè)保障,盡量避免系統(tǒng)被突發(fā)流量拖垮。zZv28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片zZv28資訊網(wǎng)——每日最新資訊28at.com

提供了四個(gè)可調(diào)用方法:zZv28資訊網(wǎng)——每日最新資訊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)}

最終調(diào)用的都是 reverveN 方法:zZv28資訊網(wǎng)——每日最新資訊28at.com

func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) bool {    // 判斷 Redis 健康狀態(tài),如果 Redis 故障,則使用進(jìn)程內(nèi)限流器    if atomic.LoadUint32(&lim.redisAlive) == 0 {        return lim.rescueLimiter.AllowN(now, n)    }    // 執(zhí)行限流腳本    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)        // 如果有異常的話,會(huì)啟動(dòng)進(jìn)程內(nèi)限流        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}

最后看一下進(jìn)程內(nèi)限流的啟動(dòng)與恢復(fù):zZv28資訊網(wǎng)——每日最新資訊28at.com

func (lim *TokenLimiter) startMonitor() {    lim.rescueLock.Lock()    defer lim.rescueLock.Unlock()    // 需要加鎖保護(hù),如果程序已經(jīng)啟動(dòng)了,直接返回,不要重復(fù)啟動(dòng)    if lim.monitorStarted {        return    }    lim.monitorStarted = true    atomic.StoreUint32(&lim.redisAlive, 0)    go lim.waitForRedis()}func (lim *TokenLimiter) waitForRedis() {    ticker := time.NewTicker(pingInterval)    // 更新監(jiān)控進(jìn)程的狀態(tài)    defer func() {        ticker.Stop()        lim.rescueLock.Lock()        lim.monitorStarted = false        lim.rescueLock.Unlock()    }()    for range ticker.C {        // 對(duì) redis 進(jìn)行健康監(jiān)測(cè),如果 redis 服務(wù)恢復(fù)了        // 則更新 redisAlive 標(biāo)識(shí),并退出 goroutine        if lim.store.Ping() {            atomic.StoreUint32(&lim.redisAlive, 1)            return        }    }}

參考文章:zZv28資訊網(wǎng)——每日最新資訊28at.com

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

本文鏈接:http://www.www897cc.com/showinfo-26-5770-0.htmlGo-Zero 是如何實(shí)現(xiàn)令牌桶限流的?

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 「Go面經(jīng)」算法 并發(fā)模型 緩存落盤 etcd actor模型

下一篇: 阿里云還會(huì)繼續(xù)降價(jià)嗎?

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
欧美精选午夜久久久乱码6080| 99精品欧美一区| 国产精品xvideos88| 国产精品免费视频xxxx| 国内成+人亚洲+欧美+综合在线| 在线激情影院一区| 一区二区三区国产盗摄| 欧美影院在线播放| 欧美激情1区2区| 国产免费成人av| 国内一区二区三区| 玖玖玖国产精品| 国产一区二区精品| 亚洲欧美国产高清va在线播| 久久精品一二三| 亚洲一区观看| 久久综合狠狠综合久久综合88| 欧美人妖在线观看| 国产一区二区三区在线播放免费观看| 亚洲黄一区二区三区| 亚洲尤物在线视频观看| 老司机免费视频一区二区三区| 欧美先锋影音| 在线看片一区| 香蕉亚洲视频| 欧美日韩三区| 亚洲成色最大综合在线| 亚洲欧美日韩国产另类专区| 欧美成人小视频| 国产一区二区无遮挡| 一区二区三区.www| 免费观看日韩| 国产午夜一区二区三区| 亚洲视频1区| 欧美劲爆第一页| 激情国产一区| 翔田千里一区二区| 国产精品久久久久毛片大屁完整版| 亚洲一区中文| 欧美国产视频在线| 黄色成人91| 亚洲欧美精品suv| 欧美日韩性视频在线| 亚洲国产一区二区三区在线播 | 午夜精品视频在线观看| 欧美日本国产视频| 亚洲高清三级视频| 久久精品国语| 国产伦精品一区二区三| 在线一区亚洲| 欧美黑人国产人伦爽爽爽| 激情综合电影网| 久久精品1区| 国产伦精品一区二区三区视频孕妇 | 亚洲校园激情| 欧美区日韩区| 亚洲精品乱码视频| 欧美mv日韩mv国产网站app| 狠狠爱综合网| 久久精品国产亚洲高清剧情介绍| 国产精品嫩草影院av蜜臀| 一本色道**综合亚洲精品蜜桃冫| 欧美成人69| 亚洲国产欧美一区二区三区丁香婷| 久久久久久成人| 国产在线不卡精品| 久久国产精品久久久久久久久久 | 国语自产精品视频在线看抢先版结局| 亚洲欧美资源在线| 国产精品一区二区三区四区| 亚洲综合国产激情另类一区| 欧美三级特黄| 在线性视频日韩欧美| 欧美日韩精品一区二区三区四区 | 久热精品在线视频| 精品成人在线视频| 久久亚洲国产精品日日av夜夜| 韩国三级在线一区| 久久久久久久久久久久久久一区 | 在线观看欧美日本| 久久这里有精品视频| 在线国产精品播放| 免费成人性网站| 亚洲国产综合在线| 欧美精品一线| 一区二区三区国产在线| 欧美色视频在线| 亚洲在线电影| 国产欧美日韩精品a在线观看| 欧美影院精品一区| 黄页网站一区| 欧美国产日韩一二三区| 亚洲精品女人| 欧美视频一区二区三区| 亚洲男人影院| 国产一区二区毛片| 久久综合色婷婷| 亚洲理伦在线| 国产精品美女www爽爽爽| 欧美一区在线直播| 在线精品视频免费观看| 欧美连裤袜在线视频| 亚洲一区二区三区在线视频| 国产乱码精品一区二区三区忘忧草| 欧美中文字幕在线视频| 亚洲国产精品久久久久秋霞影院 | 欧美激情在线观看| 中文一区二区| 国际精品欧美精品| 欧美风情在线观看| 亚洲一区二区免费在线| 国产亚洲欧洲997久久综合| 免费欧美视频| 亚洲小说欧美另类婷婷| 国产亚洲女人久久久久毛片| 女同性一区二区三区人了人一| 99亚洲伊人久久精品影院红桃| 国产精品一二三| 裸体歌舞表演一区二区| 99伊人成综合| 国模套图日韩精品一区二区| 欧美激情一区二区三级高清视频| 亚洲欧美电影院| 亚洲电影在线看| 国产精品久久久久99| 久久天天躁狠狠躁夜夜av| av成人激情| 狠狠色丁香久久综合频道| 欧美日韩大片| 久久久国产精品亚洲一区| 99国产精品久久久久久久| 国产亚洲aⅴaaaaaa毛片| 欧美大片一区二区三区| 性欧美精品高清| 亚洲精品在线视频| 国产一区二区日韩| 欧美日韩一区二区三区四区五区| 久久精品国产久精国产爱| 亚洲美女网站| 国内不卡一区二区三区| 欧美视频中文字幕| 久热综合在线亚洲精品| 亚洲免费小视频| 亚洲精品极品| 国产一区高清视频| 欧美视频免费在线| 免费成人黄色av| 久久福利视频导航| 亚洲一区二区毛片| 亚洲国产免费看| 国产在线日韩| 国产精品视频区| 欧美日韩不卡合集视频| 久久野战av| 性欧美长视频| 在线国产亚洲欧美| 亚洲国产欧美一区| 亚洲高清电影| 精品av久久久久电影| 好看不卡的中文字幕| 国产九区一区在线| 欧美精品久久久久久久| 亚洲一区二区三区欧美| 国产精品色在线| 欧美激情1区2区| 久久婷婷av| 久久激情视频久久| 午夜精品一区二区三区在线播放 | 欧美午夜精品久久久久久孕妇 | 国产精品igao视频网网址不卡日韩| 美日韩精品免费观看视频| 欧美怡红院视频| 亚洲一区二区伦理| 9i看片成人免费高清| 亚洲国产毛片完整版| 激情欧美一区二区三区在线观看| 国产伦理一区| 国产精品日韩欧美一区| 欧美性色aⅴ视频一区日韩精品| 欧美国产三区| 玖玖玖免费嫩草在线影院一区| 久久精品国产第一区二区三区最新章节 | 欧美日韩成人在线| 久久综合99re88久久爱| 久久国产精品一区二区三区四区| 亚洲午夜一区二区三区| 中文精品视频一区二区在线观看| 亚洲日本aⅴ片在线观看香蕉| 亚洲成人在线| 1204国产成人精品视频| 一色屋精品亚洲香蕉网站| 国产一区清纯| 国产综合在线看| 精久久久久久久久久久| 精品成人一区二区三区| 尤物九九久久国产精品的分类| 黄色精品在线看| 激情综合久久| 精品99视频| 在线日本欧美| 亚洲国产精品综合| 亚洲精品视频一区二区三区| 亚洲精品在线看| 一区二区成人精品|