最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解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項目中輸出版本信息

    詳解如何在Go項目中輸出版本信息

    這篇文章主要介紹了詳解如何在Go項目中輸出版本信息,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • golang中按照結(jié)構(gòu)體的某個字段排序?qū)嵗a

    golang中按照結(jié)構(gòu)體的某個字段排序?qū)嵗a

    在任何編程語言中,關(guān)乎到數(shù)據(jù)的排序都會有對應(yīng)的策略,下面這篇文章主要給大家介紹了關(guān)于golang中按照結(jié)構(gòu)體的某個字段排序的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • 詳解Golang中單元測試的使用

    詳解Golang中單元測試的使用

    單元測試是檢測你寫的一個函數(shù)是否具備安全性的一次檢測,這篇文章主要為大家詳細(xì)介紹了Golang中單元測試的具體使用,希望對大家有所幫助
    2023-07-07
  • Go語言關(guān)于幾種深度拷貝(deepcopy)方法的性能對比

    Go語言關(guān)于幾種深度拷貝(deepcopy)方法的性能對比

    這篇文章主要介紹了Go語言關(guān)于幾種深度拷貝(deepcopy)方法的性能對比,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • golang切片原理詳細(xì)解析

    golang切片原理詳細(xì)解析

    這篇文章主要介紹了golang切片原理詳細(xì)解析,切片在編譯時定義為Slice結(jié)構(gòu)體,并通過NewSlice()函數(shù)進(jìn)行創(chuàng)建,更多相關(guān)內(nèi)容感興趣的小伙伴可以參考一下下面文章內(nèi)容
    2022-06-06
  • 如何用golang運(yùn)行第一個項目

    如何用golang運(yùn)行第一個項目

    這篇文章主要介紹了如何用golang運(yùn)行第一個項目,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Go項目中正確升級第三方依賴實戰(zhàn)指南(使用Go?Modules)

    Go項目中正確升級第三方依賴實戰(zhàn)指南(使用Go?Modules)

    這篇文章主要介紹了Go項目中使用Go?Modules正確升級第三方依賴的相關(guān)資料,Go Modules是Go語言官方提供的依賴管理工具,通過去中心化設(shè)計和內(nèi)置的版本控制機(jī)制,實現(xiàn)了高效、安全的依賴管理,需要的朋友可以參考下
    2026-06-06
  • 使用Go語言實現(xiàn)發(fā)送HTTP請求并給GET添加參數(shù)

    使用Go語言實現(xiàn)發(fā)送HTTP請求并給GET添加參數(shù)

    在開發(fā)Web應(yīng)用程序時,我們經(jīng)常需要向服務(wù)器發(fā)送HTTP請求,本文將介紹一下使用Go語言發(fā)送HTTP請求,并給GET請求添加參數(shù)的方法,感興趣的小伙伴可以了解一下
    2023-07-07
  • Go中Channel發(fā)送和接收操作指南

    Go中Channel發(fā)送和接收操作指南

    在golang中channel屬于較為核心的一個功能,尤其在go協(xié)程中,channel功能尤為重要,下面這篇文章主要給大家介紹了關(guān)于Go中Channel發(fā)送和接收操作的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • 淺談Go Slice 高級實踐

    淺談Go Slice 高級實踐

    這篇文章主要介紹了淺談Go Slice 高級實踐,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08

最新評論

通州区| 仁化县| 行唐县| 长白| 新安县| 岳阳县| 资源县| 松桃| 绥化市| 偏关县| 手游| 新余市| 渝北区| 梁河县| 昌吉市| 玛沁县| 安乡县| 德兴市| 达拉特旗| 商丘市| 梧州市| 冷水江市| 金门县| 五家渠市| 秭归县| 屏边| 贵德县| 通海县| 无锡市| 鸡东县| 孝昌县| 马鞍山市| 莲花县| 盐亭县| 长葛市| 万宁市| 积石山| 陆良县| 洮南市| 巴东县| 巫山县|