AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

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

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

來(lái)源: 責(zé)編: 時(shí)間:2023-08-14 22:01:53 3285觀看
導(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)的源碼。3HH28資訊網(wǎng)——每日最新資訊28at.com

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

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

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

工作原理

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

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

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

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

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

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

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

限流器初始化:3HH28資訊網(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ā)流量拖垮。3HH28資訊網(wǎng)——每日最新資訊28at.com

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

提供了四個(gè)可調(diào)用方法:3HH28資訊網(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 方法:3HH28資訊網(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ù):3HH28資訊網(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        }    }}

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

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

本文鏈接:http://www.tebozhan.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)
  • 紅魔電競(jìng)平板評(píng)測(cè):大屏幕硬實(shí)力

    前言:三年的疫情因?yàn)橐暇W(wǎng)課的原因激活了平板市場(chǎng),如今網(wǎng)課的時(shí)代已經(jīng)過(guò)去,大家的生活都恢復(fù)到了正軌,這也就意味著,真正考驗(yàn)平板電腦生存的環(huán)境來(lái)了。也就是面對(duì)著這種殘酷的
  • 石頭自清潔掃拖機(jī)器人G10S評(píng)測(cè):多年黑科技集大成之作 懶人終極福音

    科技圈經(jīng)常能看到一個(gè)詞叫“縫合怪”,用來(lái)形容那些把好多功能或者外觀結(jié)合在一起的產(chǎn)品,通常這樣的詞是貶義詞,但如果真的是產(chǎn)品縫合的好、縫合的實(shí)用的話,那它就成了中性詞,今
  • K6:面向開(kāi)發(fā)人員的現(xiàn)代負(fù)載測(cè)試工具

    K6 是一個(gè)開(kāi)源負(fù)載測(cè)試工具,可以輕松編寫、運(yùn)行和分析性能測(cè)試。它建立在 Go 和 JavaScript 之上,它被設(shè)計(jì)為功能強(qiáng)大、可擴(kuò)展且易于使用。k6 可用于測(cè)試各種應(yīng)用程序,包括 Web
  • 線程通訊的三種方法!通俗易懂

    線程通信是指多個(gè)線程之間通過(guò)某種機(jī)制進(jìn)行協(xié)調(diào)和交互,例如,線程等待和通知機(jī)制就是線程通訊的主要手段之一。 在 Java 中,線程等待和通知的實(shí)現(xiàn)手段有以下幾種方式:Object 類下
  • 三言兩語(yǔ)說(shuō)透設(shè)計(jì)模式的藝術(shù)-單例模式

    寫在前面單例模式是一種常用的軟件設(shè)計(jì)模式,它所創(chuàng)建的對(duì)象只有一個(gè)實(shí)例,且該實(shí)例易于被外界訪問(wèn)。單例對(duì)象由于只有一個(gè)實(shí)例,所以它可以方便地被系統(tǒng)中的其他對(duì)象共享,從而減少
  • 自動(dòng)化在DevOps中的力量:簡(jiǎn)化軟件開(kāi)發(fā)和交付

    自動(dòng)化在DevOps中扮演著重要角色,它提升了DevOps的效能。通過(guò)自動(dòng)化工具和方法,DevOps團(tuán)隊(duì)可以實(shí)現(xiàn)以下目標(biāo):消除手動(dòng)和重復(fù)性任務(wù)。簡(jiǎn)化流程。在整個(gè)軟件開(kāi)發(fā)生命周期中實(shí)現(xiàn)更
  • 小米MIX Fold 3下月亮相:今年唯一無(wú)短板的全能折疊屏

    這段時(shí)間以來(lái),包括三星、一加、榮耀等等有不少品牌旗下的最新折疊屏旗艦都有新的進(jìn)展,其中榮耀、三星都已陸續(xù)發(fā)布了最新的折疊屏旗艦,尤其號(hào)榮耀Magi
  • 華為HarmonyOS 4.0將于8月4日發(fā)布 或搭載AI大模型技術(shù)

    華為宣布HarmonyOS4.0將于8月4日正式發(fā)布。此前,華為已經(jīng)針對(duì)開(kāi)發(fā)者公布了HarmonyOS4.0,以便于開(kāi)發(fā)者提前進(jìn)行適配,也因此被曝光出了一些新系統(tǒng)的特性
  • 親歷馬斯克血洗Twitter,硅谷的苦日子在后頭

    文/劉哲銘  編輯/李薇  馬斯克再次揮下裁員大刀。  美國(guó)時(shí)間11月14日,Twitter約4400名外包員工遭解雇,此次被解雇的員工的主要工作為內(nèi)容審核等。此前,T
Top