精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

開發(fā) 前端
生成的令牌放入令牌桶中存放,如果令牌桶滿了則多余的令牌會直接丟棄,當(dāng)請求到達(dá)時(shí),會嘗試從令牌桶中取令牌,取到了令牌的請求可以執(zhí)行; 如果桶空了,那么嘗試取令牌的請求會被直接丟棄。

上一篇文章介紹了 如何實(shí)現(xiàn)計(jì)數(shù)器限流。主要有兩種實(shí)現(xiàn)方式,分別是固定窗口和滑動窗口,并且分析了 go-zero 采用固定窗口方式實(shí)現(xiàn)的源碼。

但是采用固定窗口實(shí)現(xiàn)的限流器會有兩個(gè)問題:

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

這篇文章來介紹一下令牌桶算法,可以很好解決以上兩個(gè)問題。

工作原理

算法概念如下:

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

圖片圖片

令牌桶算法既能夠?qū)⑺械恼埱笃骄植嫉綍r(shí)間區(qū)間內(nèi),又能接受服務(wù)器能夠承受范圍內(nèi)的突發(fā)請求,因此是目前使用較為廣泛的一種限流算法。

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

源碼分析我們還是以 go-zero 項(xiàng)目為例,首先來看生成令牌的部分,依然是使用 Redis 來實(shí)現(xiàn)。

// core/limit/tokenlimit.go

// 生成 token 速率
script = `local rate = tonumber(ARGV[1])
// 通容量
local capacity = tonumber(ARGV[2])
// 當(dāng)前時(shí)間戳
local now = tonumber(ARGV[3])
// 請求數(shù)量
local requested = tonumber(ARGV[4])
// 需要多少秒才能把桶填滿
local fill_time = capacity/rate
// 向下取整,ttl 為填滿時(shí)間 2 倍
local ttl = math.floor(fill_time*2)
// 當(dāng)前桶剩余容量,如果為 nil,說明第一次使用,賦值為桶最大容量
local last_tokens = tonumber(redis.call("get", KEYS[1]))
if last_tokens == nil then
    last_tokens = capacity
end

// 上次請求時(shí)間戳,如果為 nil 則賦值 0
local last_refreshed = tonumber(redis.call("get", KEYS[2]))
if last_refreshed == nil then
    last_refreshed = 0
end

// 距離上一次請求的時(shí)間跨度
local delta = math.max(0, now-last_refreshed)
// 距離上一次請求的時(shí)間跨度能生成的 token 數(shù)量和桶內(nèi)剩余 token 數(shù)量的和
// 與桶容量比較,取二者的小值
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
// 判斷請求數(shù)量和桶內(nèi) token 數(shù)量的大小
local allowed = filled_tokens >= requested
// 被請求消耗掉之后,更新剩余 token 數(shù)量
local new_tokens = filled_tokens
if allowed then
    new_tokens = filled_tokens - requested
end

// 更新 redis token
redis.call("setex", KEYS[1], ttl, new_tokens)
// 更新 redis 刷新時(shí)間
redis.call("setex", KEYS[2], ttl, now)

return allowed`

Redis 中主要保存兩個(gè) key,分別是 token 數(shù)量和刷新時(shí)間。

核心思想就是比較兩次請求時(shí)間間隔內(nèi)生成的 token 數(shù)量 + 桶內(nèi)剩余 token 數(shù)量,和請求量之間的大小,如果滿足則允許,否則則不允許。

限流器初始化:

// 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)識
    redisAlive     uint32
    // redis 健康監(jiān)控啟動狀態(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ā)流量拖垮。

圖片圖片

提供了四個(gè)可調(diào)用方法:

// 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 方法:

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)
        // 如果有異常的話,會啟動進(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)限流的啟動與恢復(fù):

func (lim *TokenLimiter) startMonitor() {
    lim.rescueLock.Lock()
    defer lim.rescueLock.Unlock()

    // 需要加鎖保護(hù),如果程序已經(jīng)啟動了,直接返回,不要重復(fù)啟動
    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 {
        // 對 redis 進(jìn)行健康監(jiān)測,如果 redis 服務(wù)恢復(fù)了
        // 則更新 redisAlive 標(biāo)識,并退出 goroutine
        if lim.store.Ping() {
            atomic.StoreUint32(&lim.redisAlive, 1)
            return
        }
    }
}

參考文章:

責(zé)任編輯:武曉燕 來源: AlwaysBeta
相關(guān)推薦

2023-08-07 08:01:15

2022-01-12 12:46:32

Go限流保障

2023-08-28 08:00:45

2020-10-16 09:34:39

漏桶令牌桶限流

2025-05-26 04:00:00

2024-04-28 14:46:55

gozero微服務(wù)技巧

2024-11-05 15:02:41

2025-05-23 10:10:00

限流算法系統(tǒng)Go

2021-05-31 07:01:46

限流算法令牌

2024-12-25 15:44:15

2024-02-04 10:08:34

2025-04-08 09:20:00

Sentinel限流微服務(wù)

2023-02-20 08:08:48

限流算法計(jì)數(shù)器算法令牌桶算法

2025-01-21 08:31:12

2022-05-19 14:14:26

go語言限流算法

2023-10-16 16:00:27

Redis限流

2025-08-27 02:15:00

2025-07-01 02:00:00

Spring接口限流高并發(fā)

2023-11-20 10:09:59

2021-03-30 10:46:42

SpringBoot計(jì)數(shù)器漏桶算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

久久久久久久av| 亚洲国产精品久久久久婷婷884| 国产精品xxx视频| 日韩三级精品电影久久久| 国产成人在线亚洲欧美| 久久一级免费视频| 给我免费播放日韩视频| 在线视频你懂得一区| 久久久成人精品一区二区三区| 日韩一级片免费看| 免费观看在线色综合| 欧美日韩成人在线观看| 少妇无套高潮一二三区| theporn国产在线精品| 欧美三区在线观看| 99精品在线免费视频| 影音先锋亚洲精品| 五月天丁香久久| 深田咏美在线x99av| 亚洲AV无码一区二区三区少妇 | 久久久综合久久久| 精品一区二区三区在线| 亚洲国产精品人人爽夜夜爽| 奇米视频7777| 浪潮色综合久久天堂| 亚洲综合色在线| 亚洲综合第一| 精品乱码一区二区三四区视频| 国产精品18久久久久久久网站| 国产欧美精品一区二区色综合 | 凹凸日日摸日日碰夜夜爽1| 免费av网站在线观看| 久久久精品一品道一区| 国产日产精品一区二区三区四区| 国产精品无码在线播放| 日本中文字幕不卡| 欧美与黑人午夜性猛交久久久| 国产黄色片在线免费观看| 欧美日韩一二三四| 亚洲日本aⅴ片在线观看香蕉| 国产精品果冻传媒| 亚洲**毛片| 日韩亚洲欧美中文三级| 中文字幕一区二区在线观看视频| av在线日韩| 色噜噜狠狠色综合中国| 成人免费在线小视频| 久热在线观看视频| 欧美日韩国产中文精品字幕自在自线| 五月天激情图片| 羞羞电影在线观看www| 成人欧美一区二区三区视频网页| 亚洲草草视频| 91女主播在线观看| 亚洲国产高清在线观看视频| 99久久99久久精品免费看蜜桃| 国产精品高潮呻吟久久av野狼 | 欧美性猛交xxxx富婆| 一卡二卡三卡视频| av老司机在线观看| 午夜视频在线观看一区二区 | 欧美中文日韩| 欧美激情在线一区| 黄网站免费在线| 精品91在线| 97热精品视频官网| 国产成人无码一区二区在线播放| 国产精品一区毛片| 全球成人中文在线| 一级特黄免费视频| 狠狠色丁香婷综合久久| 99国精产品一二二线| 人妻一区二区三区免费| 99精品国产99久久久久久白柏| 久久青青草原一区二区| 国产精品一区在线看| 国产精品你懂的在线欣赏| 在线观看亚洲视频啊啊啊啊| 午夜成年人在线免费视频| 亚洲成人在线观看视频| 欧美成人精品欧美一级乱| 国产精品久久久久久久久久齐齐| 欧美影院一区二区| 日本成人在线免费观看| 日韩成人av在线资源| 国产亚洲精品久久久久久| 日韩精品久久久久久久的张开腿让| 久久久久久久久久久妇女| 欧美高清无遮挡| 天天操夜夜操视频| 色播色播色播色播色播在线| 亚洲精品人人| 国产成人精品久久久| 97超碰国产在线| 成人国产精品免费观看视频| 欧美日韩电影一区二区三区| 黄色精品免费看| 午夜精品一区二区三区三上悠亚 | 动漫性做爰视频| 99精品国产一区二区青青牛奶| 国产精品扒开腿做爽爽爽视频| 国产av无码专区亚洲av麻豆| 91小视频在线| 免费看污污视频| 电影网一区二区| 欧美成人精品1314www| 亚洲av熟女国产一区二区性色| 欧美精品一级| 国产精品天天狠天天看| 熟妇人妻系列aⅴ无码专区友真希| 亚洲国产精品成人综合色在线婷婷 | 欧美一区二区黄片| 国产精品美女www爽爽爽| 国产精品999视频| 精品一区二区三区亚洲| 亚洲色图在线观看| 日韩欧美三级在线观看| 国产又粗又长视频| 国产一区高清在线| 欧美一区二区在线| 电影午夜精品一区二区三区| 亚洲一级黄色大片| 91免费观看视频| 久久精品在线免费视频| 亚洲夂夂婷婷色拍ww47| 日本888xxxx| 欧美日韩破处| 欧美激情第1页| 国产一区二区视频免费观看| 久久夜色精品一区| 男人添女荫道口图片| 久久丁香四色| 久久精品国产电影| 中文字幕av资源| 久久亚洲精精品中文字幕早川悠里| 黑人巨茎大战欧美白妇| 91精品视频一区二区| 在线播放国产一区中文字幕剧情欧美| 国产精品16p| 国产成人99久久亚洲综合精品| 在线视频亚洲自拍| 国产精品久久久久久久久免费高清| 亚洲欧洲日韩国产| 伊人中文字幕在线观看| 2017欧美狠狠色| 人妻熟女一二三区夜夜爱| 日韩激情网站| 欧美一区二粉嫩精品国产一线天| 人妻中文字幕一区| 久久亚洲资源中文字| 精品国产99国产精品| 青青草免费av| 最新国产中文字幕| 日韩精品免费专区| av在线播放网站| 亚洲欧美另类久久久精品2019| 老头吃奶性行交视频| 久久99视频| 国产精品极品尤物在线观看| 国产精品秘入口| 欧美日高清视频| 91日韩中文字幕| av在线免费不卡| 欧美国产亚洲一区| 欧洲美女日日| 国产一区二区视频在线观看| 欧美日韩在线资源| 欧美一区二区三级| 国产无套粉嫩白浆内谢| 99re这里都是精品| 久久久久国产精品熟女影院| 日韩中文在线电影| 91手机在线视频| av漫画网站在线观看| 日韩a在线看| 日本韩国欧美在线| 青青青视频在线播放| 国内精品伊人久久久久av一坑| 国产在线无码精品| 精品视频自拍| 中文字幕日本在线观看| 亚洲天堂成人网| 色男人天堂av| 欧美性受xxxxxx黑人xyx性爽| 97成人在线观看| 欧美经典一区二区| 久久aaaa片一区二区| 99精品视频网| 亚洲一区不卡在线| 国产成人在线中文字幕| 国产黑人绿帽在线第一区| 午夜精品一区| 亚洲精品美女免费| 一区不卡在线观看| 天天亚洲美女在线视频| 成人小视频免费看| 福利一区二区在线| 日韩av片网站| 亚洲免费激情| 欧美日韩亚洲国产成人| 亚洲区小说区图片区qvod按摩| 成人黄色网免费| 亚洲性色av| 欧美超级免费视 在线| 蜜桃视频在线免费| 欧美xxxxxxxxx| 亚洲天堂网在线视频| 天天色天天操综合| 精品国产国产综合精品| 91蜜桃在线免费视频| 古装做爰无遮挡三级聊斋艳谭| 视频一区在线播放| 欧美激情视频免费看| 亚洲成人二区| 色一情一乱一伦一区二区三区丨| 国产成人av毛片| 成人午夜黄色影院| 超薄肉色丝袜脚交一区二区| 97超级碰在线看视频免费在线看 | 不卡视频免费播放| 成年人三级黄色片| 国内精品国产成人国产三级| 亚洲一区二区三区中文字幕| 国产精品情侣呻吟对白视频| 伊人精品综合| 国产清纯白嫩初高生在线观看91 | 亚洲国产成人精品无码区99| 国产精品毛片久久| 亚洲免费不卡| 精品视频国产| 日本在线观看一区二区三区| 欧美电影在线观看完整版| 懂色av一区二区三区在线播放| 欧美网站免费| 国产免费一区视频观看免费| 欧美××××黑人××性爽| 日本不卡免费高清视频| 偷拍自拍在线看| 欧洲永久精品大片ww免费漫画| zzzwww在线看片免费| 无码视频在线观看| 91免费在线播放| 大桥未久恸哭の女教师| 国产一区二区美女诱惑| 成人不卡免费视频| 蜜臀91精品一区二区三区| 在线观看的毛片| 美国毛片一区二区三区| 一区二区成人网| 美女在线一区二区| 天天摸天天舔天天操| 精品一区二区三区香蕉蜜桃 | 久久久精品人妻一区二区三区四| 中文字幕佐山爱一区二区免费| 91ts人妖另类精品系列| 国产精品高潮呻吟久久| 欧美日韩午夜视频| 亚洲摸摸操操av| 一区二区免费电影| 亚洲区小说区图片区| 亚洲国产高清福利视频| 日韩在线视频免费| 精品无人区乱码1区2区3区在线| 日本大臀精品| 在线视频国产日韩| 成人午夜在线影视| 久久人人看视频| 欧美一级大片| 国产精品永久在线| 亚洲三级av| 欧美黑人3p| 水蜜桃久久夜色精品一区| 国产经典久久久| 99在线热播精品免费99热| 成人精品小视频| 精品制服美女久久| 任你躁av一区二区三区| 久久综合九色综合欧美亚洲| 国产调教在线观看| 一区二区三区欧美久久| 国产精品视频久久久久久久| 欧洲视频一区二区| av高清一区二区| 国产视频亚洲精品| 成人午夜在线影视| 日韩av免费看| 国产激情一区| 欧美二区在线| 欧美一区不卡| 熟妇人妻无乱码中文字幕真矢织江| 精品一区二区精品| free性中国hd国语露脸| 国产精品盗摄一区二区三区| 黄色小说在线观看视频| 欧美日韩中文另类| 欧美特黄一级视频| 色哟哟网站入口亚洲精品| 1234区中文字幕在线观看| 极品少妇xxxx精品少妇偷拍| 欧美性猛交乱大交| 国产午夜精品一区二区| 久久网免费视频| 麻豆视频在线| 麻豆乱码国产一区二区三区| 午夜影视一区二区三区| 亚洲va久久久噜噜噜久久天堂| 人人香蕉久久| 9191国产视频| 美国一区二区三区在线播放 | 欧美美女操人视频| 日韩和的一区二在线| 国产一区再线| 欧美在线网站| 天堂一区在线观看| 久久色中文字幕| 国产一级久久久| 91麻豆精品国产91久久久久| 国产一级二级三级在线观看| 97国产精品免费视频| 久久久国产精品入口麻豆| 日本一区美女| 欧美中文日韩| 日本黄色录像片| 亚洲一区中文日韩| 国产欧美久久久精品免费| 欲色天天网综合久久| 久久青青视频| 久久精品国产精品国产精品污| 欧美另类视频| 中文字幕欧美视频| 亚洲三级免费电影| 国产精品一级视频| 深夜福利一区二区| 欧美一区=区三区| 台湾成人av| 青青草伊人久久| 久操视频在线观看免费| 色欧美日韩亚洲| 黄色在线视频观看网站| 欧美在线影院在线视频| 日本精品影院| 免费在线a视频| 91免费看`日韩一区二区| 你懂的国产视频| 亚洲精品美女在线观看播放| 精精国产xxx在线视频app| 国产午夜精品一区| 一区二区高清| 久久精品国产精品亚洲毛片| 亚洲日韩中文字幕| 日韩欧美国产麻豆| 欧洲毛片在线| 日本视频久久久| 国产精选一区| 少妇人妻互换不带套| 国产视频不卡一区| 超碰在线97观看| 最新的欧美黄色| 国产一区二区高清在线| 人人妻人人澡人人爽欧美一区 | 午夜精品福利一区二区三区蜜桃| 天堂√在线观看一区二区| 日本道精品一区二区三区| 视频二区在线| 国产成人精品最新| 日韩中文首页| 精品人妻一区二区三区免费| 樱花草国产18久久久久| 天天射,天天干| 国产极品jizzhd欧美| 国产精品久久久久蜜臀| 三上悠亚 电影| 精品高清美女精品国产区| 好男人免费精品视频| 国产日韩av在线播放| 黄色国产精品| 亚洲做受高潮无遮挡| 欧美日韩日本视频| 天堂8中文在线| 欧美亚洲另类在线一区二区三区| 久久精品国产99久久6| 国产一级淫片免费| 国产亚洲视频中文字幕视频| 二区三区精品| 国产91在线视频观看| 国产精品久久久久一区二区三区共| 国产黄色片av| 日本三级久久久| 欧美成人日韩| 精品无码一区二区三区 | 日本少妇高潮喷水视频| 国产精品久久久久永久免费观看 | 天天干天天综合| 亚洲伊人色欲综合网| 浮生影视网在线观看免费| 91av免费看| 日本不卡在线视频| 日本一区二区网站| xxxxx成人.com| 美女亚洲一区|