欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解go-zero如何實現(xiàn)計數(shù)器限流

 更新時間:2023年08月08日 09:41:23   作者:AlwaysBeta  
這篇文章主要來和大家說說限流,主要包括計數(shù)器限流算法以及具體的代碼實現(xiàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

上一篇文章 go-zero 是如何做路由管理的? 介紹了路由管理,這篇文章來說說限流,主要介紹計數(shù)器限流算法,具體的代碼實現(xiàn),我們還是來分析微服務(wù)框架 go-zero 的源碼。

在微服務(wù)架構(gòu)中,一個服務(wù)可能需要頻繁地與其他服務(wù)交互,而過多的請求可能導(dǎo)致性能下降或系統(tǒng)崩潰。為了確保系統(tǒng)的穩(wěn)定性和高可用性,限流算法應(yīng)運(yùn)而生。

限流算法允許在給定時間段內(nèi),對服務(wù)的請求流量進(jìn)行控制和調(diào)整,以防止資源耗盡和服務(wù)過載。

計數(shù)器限流算法主要有兩種實現(xiàn)方式,分別是:

  • 固定窗口計數(shù)器
  • 滑動窗口計數(shù)器

下面分別來介紹。

固定窗口計數(shù)器

算法概念如下:

  • 將時間劃分為多個窗口;
  • 在每個窗口內(nèi)每有一次請求就將計數(shù)器加一;
  • 如果計數(shù)器超過了限制數(shù)量,則本窗口內(nèi)所有的請求都被丟棄當(dāng)時間到達(dá)下一個窗口時,計數(shù)器重置。

固定窗口計數(shù)器是最為簡單的算法,但這個算法有時會讓通過請求量允許為限制的兩倍。

考慮如下情況:限制 1 秒內(nèi)最多通過 5 個請求,在第一個窗口的最后半秒內(nèi)通過了 5 個請求,第二個窗口的前半秒內(nèi)又通過了 5 個請求。這樣看來就是在 1 秒內(nèi)通過了 10 個請求。

滑動窗口計數(shù)器

算法概念如下:

  • 將時間劃分為多個區(qū)間;
  • 在每個區(qū)間內(nèi)每有一次請求就將計數(shù)器加一維持一個時間窗口,占據(jù)多個區(qū)間;
  • 每經(jīng)過一個區(qū)間的時間,則拋棄最老的一個區(qū)間,并納入最新的一個區(qū)間;
  • 如果當(dāng)前窗口內(nèi)區(qū)間的請求計數(shù)總和超過了限制數(shù)量,則本窗口內(nèi)所有的請求都被丟棄。

滑動窗口計數(shù)器是通過將窗口再細(xì)分,并且按照時間滑動,這種算法避免了固定窗口計數(shù)器帶來的雙倍突發(fā)請求,但時間區(qū)間的精度越高,算法所需的空間容量就越大。

go-zero 實現(xiàn)

go-zero 實現(xiàn)的是固定窗口的方式,計算一段時間內(nèi)對同一個資源的訪問次數(shù),如果超過指定的 limit,則拒絕訪問。當(dāng)然如果在一段時間內(nèi)訪問不同的資源,每一個資源訪問量都不超過 limit,此種情況是不會拒絕的。

而在一個分布式系統(tǒng)中,存在多個微服務(wù)提供服務(wù)。所以當(dāng)瞬間的流量同時訪問同一個資源,如何讓計數(shù)器在分布式系統(tǒng)中正常計數(shù)?

這里要解決的一個主要問題就是計算的原子性,保證多個計算都能得到正確結(jié)果。

通過以下兩個方面來解決:

  • 使用 redis 的 incrby 做資源訪問計數(shù)
  • 采用 lua script 做整個窗口計算,保證計算的原子性

接下來先看一下 lua script 的源碼:

//?core/limit/periodlimit.go
const?periodScript?=?`local?limit?=?tonumber(ARGV[1])
local?window?=?tonumber(ARGV[2])
local?current?=?redis.call("INCRBY",?KEYS[1],?1)
if?current?==?1?then
????redis.call("expire",?KEYS[1],?window)
end
if?current?<?limit?then
????return?1
elseif?current?==?limit?then
????return?2
else
????return?0
end`

主要就是使用 INCRBY 命令來實現(xiàn),第一次請求需要給 key 加上一個過期時間,到達(dá)過期時間之后,key 過期被清楚,重新計數(shù)。

限流器初始化:

type?(
????//?PeriodOption?defines?the?method?to?customize?a?PeriodLimit.
????PeriodOption?func(l?*PeriodLimit)
????//?A?PeriodLimit?is?used?to?limit?requests?during?a?period?of?time.
????PeriodLimit?struct?{
????????period?????int??//?窗口大小,單位?s
????????quota??????int??//?請求上限
????????limitStore?*redis.Redis
????????keyPrefix??string???//?key?前綴
????????align??????bool
????}
)
//?NewPeriodLimit?returns?a?PeriodLimit?with?given?parameters.
func?NewPeriodLimit(period,?quota?int,?limitStore?*redis.Redis,?keyPrefix?string,
????opts?...PeriodOption)?*PeriodLimit?{
????limiter?:=?&PeriodLimit{
????????period:?????period,
????????quota:??????quota,
????????limitStore:?limitStore,
????????keyPrefix:??keyPrefix,
????}
????for?_,?opt?:=?range?opts?{
????????opt(limiter)
????}
????return?limiter
}

調(diào)用限流:

//?key?就是需要被限制的資源標(biāo)識
func?(h?*PeriodLimit)?Take(key?string)?(int,?error)?{
????return?h.TakeCtx(context.Background(),?key)
}
//?TakeCtx?requests?a?permit?with?context,?it?returns?the?permit?state.
func?(h?*PeriodLimit)?TakeCtx(ctx?context.Context,?key?string)?(int,?error)?{
????resp,?err?:=?h.limitStore.EvalCtx(ctx,?periodScript,?[]string{h.keyPrefix?+?key},?[]string{
????????strconv.Itoa(h.quota),
????????strconv.Itoa(h.calcExpireSeconds()),
????})
????if?err?!=?nil?{
????????return?Unknown,?err
????}
????code,?ok?:=?resp.(int64)
????if?!ok?{
????????return?Unknown,?ErrUnknownCode
????}
????switch?code?{
????case?internalOverQuota:?//?超過上限
????????return?OverQuota,?nil
????case?internalAllowed:???//?未超過,允許訪問
????????return?Allowed,?nil
????case?internalHitQuota:??//?正好達(dá)到限流上限
????????return?HitQuota,?nil
????default:
????????return?Unknown,?ErrUnknownCode
????}
}

到此這篇關(guān)于詳解go-zero如何實現(xiàn)計數(shù)器限流的文章就介紹到這了,更多相關(guān)go-zero計數(shù)器限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go+Lua解決Redis秒殺中庫存與超賣問題

    Go+Lua解決Redis秒殺中庫存與超賣問題

    本文主要介紹了Go+Lua解決Redis秒殺中庫存與超賣問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Go實踐反向代理ReverseProxy解析

    Go實踐反向代理ReverseProxy解析

    這篇文章主要為大家介紹了Go實踐反向代理示例ReverseProxy解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • golang開發(fā)微框架Gin的安裝測試及簡介

    golang開發(fā)微框架Gin的安裝測試及簡介

    這篇文章主要為大家介紹了golang微框架Gin的安裝測試及簡介,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2021-11-11
  • Gotify搭建你的消息推送系統(tǒng)

    Gotify搭建你的消息推送系統(tǒng)

    這篇文章主要介紹了Gotify搭建你的消息推送系統(tǒng),今天要分享的是 gotify,是一個用 go 編寫的消息服務(wù)端,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2024-01-01
  • golang將多路復(fù)異步io轉(zhuǎn)成阻塞io的方法詳解

    golang將多路復(fù)異步io轉(zhuǎn)成阻塞io的方法詳解

    常見的IO模型有阻塞、非阻塞、IO多路復(fù)用,異,下面這篇文章主要給大家介紹了關(guān)于golang將多路復(fù)異步io轉(zhuǎn)成阻塞io的方法,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • Golang實現(xiàn)字符串倒序的幾種解決方案

    Golang實現(xiàn)字符串倒序的幾種解決方案

    給定一個字符串,按單詞將該字符串逆序是我們大家在開發(fā)中可能會遇到的一個需求,所以下面這篇文章主要給大家介紹了關(guān)于Golang如何實現(xiàn)字符串倒序的幾種解決方案,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-10-10
  • 深入理解Golang中的Protocol Buffers及其應(yīng)用

    深入理解Golang中的Protocol Buffers及其應(yīng)用

    本篇文章將深入探討 Go 語言中使用 Protobuf 的基礎(chǔ)知識、常見應(yīng)用以及最佳實踐,希望能幫大家了解如何在項目中高效利用 Protobuf
    2024-11-11
  • 如何通過Golang的container/list實現(xiàn)LRU緩存算法

    如何通過Golang的container/list實現(xiàn)LRU緩存算法

    文章介紹了Go語言中container/list包實現(xiàn)的雙向鏈表,并探討了如何使用鏈表實現(xiàn)LRU緩存,LRU緩存通過維護(hù)一個雙向鏈表來管理數(shù)據(jù),確保在插入和刪除操作時能夠以O(shè)(1)的平均時間復(fù)雜度運(yùn)行,提供了鏈表的操作和使用場景,并附帶了實現(xiàn)LRU緩存的代碼示例,感興趣的朋友一起看看吧
    2025-03-03
  • golang 實現(xiàn)比特幣內(nèi)核之處理橢圓曲線中的天文數(shù)字

    golang 實現(xiàn)比特幣內(nèi)核之處理橢圓曲線中的天文數(shù)字

    比特幣密碼學(xué)中涉及到的大數(shù)運(yùn)算超出常規(guī)整數(shù)范圍,需使用golang的big包進(jìn)行處理,通過使用big.Int類型,能有效避免整數(shù)溢出,并保持邏輯正確性,測試展示了在不同質(zhì)數(shù)模下的運(yùn)算結(jié)果,驗證了邏輯的準(zhǔn)確性,此外,探討了費馬小定理在有限字段除法運(yùn)算中的應(yīng)用
    2024-11-11
  • Go?select使用與底層原理講解

    Go?select使用與底層原理講解

    這篇文章主要介紹了Go?select使用與底層原理講解,select是Go提供的IO多路復(fù)用機(jī)制,可以用多個cas同時監(jiān)聽多個channl的讀寫狀態(tài),相關(guān)內(nèi)容需要的朋友可以參考一下
    2022-07-07

最新評論