go rate 原生標(biāo)準(zhǔn)限速庫(kù)的使用
在我們實(shí)際生產(chǎn)開(kāi)發(fā)過(guò)程中,不免存在一些高并發(fā)的場(chǎng)景,此時(shí)我們就需要對(duì)請(qǐng)求進(jìn)行限流,避免過(guò)高的QPS,影響我們服務(wù)器,導(dǎo)致服務(wù)出現(xiàn)波動(dòng)或不可用。
- 對(duì)于go開(kāi)發(fā)人員而言,golang標(biāo)準(zhǔn)庫(kù)就已經(jīng)為我們提供了現(xiàn)成的類(lèi)庫(kù)
golang.org/x/time/rate,我們只需直接調(diào)用即可。下面將為大家介紹該類(lèi)庫(kù)的詳細(xì)用法。
文中代碼地址,歡迎大家??:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-rate
介紹
限流(Rate Limiting)是控制對(duì)某些資源訪(fǎng)問(wèn)頻率的一種技術(shù)手段。在高并發(fā)的服務(wù)中,限流機(jī)制可以有效防止資源過(guò)載、服務(wù)崩潰,保障系統(tǒng)的穩(wěn)定性和可用性。Golang 官方標(biāo)準(zhǔn)庫(kù) golang.org/x/time/rate 提供了一個(gè)高效且易用的限流器(Rate Limiter),可以幫助開(kāi)發(fā)者方便地實(shí)現(xiàn)限流功能。
原理:golang官方的類(lèi)庫(kù)采用了令牌桶算法進(jìn)行限流。
- 令牌桶算法(Token Bucket Algorithm):是一種常用的限流算法,它通過(guò)在固定時(shí)間間隔內(nèi)向“桶”中添加“令牌”,請(qǐng)求在處理前需要從桶中獲取令牌。如果桶中有足夠的令牌,請(qǐng)求被處理;否則,請(qǐng)求被拒絕或等待。
- 速率(Rate):速率定義了令牌添加的速度,即每秒向桶中添加多少令牌。
- 容量(Burst):容量定義了桶的大小,即桶中最多可以存儲(chǔ)多少令牌。它決定了在一段時(shí)間內(nèi)允許的最大突發(fā)請(qǐng)求數(shù)。
安裝
go get golang.org/x/time/rate
API介紹
rate.NewLimiter:創(chuàng)建限流器
創(chuàng)建限流器后,可以通過(guò) Allow、Reserve、Wait 等方法請(qǐng)求許可
package main
import (
"fmt"
"golang.org/x/time/rate"
"time"
)
func main() {
// 每秒生成3個(gè)令牌,桶的容量為10個(gè)令牌
// 相當(dāng)于每秒最多能處理三個(gè)請(qǐng)求,順時(shí)并發(fā)最多能處理10個(gè)請(qǐng)求
limiter := rate.NewLimiter(3, 10)
fmt.Println("Limiter created with rate 3 tokens per second and burst size of 10")
}
limiter.Allow():請(qǐng)求是否被允許/限流
Allow 方法立即返回一個(gè)布爾值,指示請(qǐng)求是否被允許
if limiter.Allow() {
fmt.Println("Request allowed")
} else {
fmt.Println("Request denied")
}
limiter.Reserve():返回值包含了許可時(shí)間和是否可用的信息
Reserve 方法返回一個(gè) Reservation 對(duì)象,包含了許可時(shí)間和是否可用的信息
reservation := limiter.Reserve()
if reservation.OK() {
fmt.Println("Request reserved, delay:", reservation.Delay())
} else {
fmt.Println("Request cannot be reserved")
}
limiter.Wait(ctx):阻塞當(dāng)前協(xié)程,直到允許請(qǐng)求或上下文取消
Wait 方法阻塞當(dāng)前協(xié)程,直到允許請(qǐng)求或上下文取消
ctx := context.Background()
if err := limiter.Wait(ctx); err == nil {
fmt.Println("Request allowed after wait")
} else {
fmt.Println("Request denied:", err)
}
實(shí)戰(zhàn)使用
并發(fā)處理任務(wù)
一段時(shí)間內(nèi),限制服務(wù)器處理任務(wù)數(shù)
// Demo_CurrentHandleWorks 模擬并發(fā)處理任務(wù)
func Demo_CurrentHandleWorks() {
var wg sync.WaitGroup
numWorkers := 5 // 模擬5個(gè)并發(fā)請(qǐng)求
// 構(gòu)造限流器:每10s向桶中新增一個(gè)令牌,桶里最多能存放2個(gè)令牌 => 每10s能處理一個(gè)任務(wù),一定時(shí)間內(nèi)最多能處理2個(gè)任務(wù)(令牌有剩余)
var l = rate.NewLimiter(rate.Every(time.Second*10), 2)
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, &wg, l)
}
wg.Wait()
}
func worker(id int, wg *sync.WaitGroup, limiter *rate.Limiter) {
defer wg.Done()
if limiter.Allow() {
fmt.Printf("Worker %d processed at %s\n", id, time.Now().Format("15:04:05.000"))
} else {
fmt.Printf("Worker %d rejected at %s\n", id, time.Now().Format("15:04:05.000"))
}
}
HTTP服務(wù)請(qǐng)求限流
當(dāng)我們通過(guò)jmeter/腳本/連續(xù)刷新頁(yè)面并發(fā)請(qǐng)求測(cè)試時(shí),發(fā)現(xiàn)在1s內(nèi),服務(wù)器最多只能處理2個(gè)請(qǐng)求,其余請(qǐng)求都不會(huì)被處理,會(huì)返回Too Many Requests。
// Demo_CurrentHandleWithHTTP 模擬HTTP請(qǐng)求限流
func Demo_CurrentHandleWithHTTP() {
// 每秒最多處理 1 個(gè)請(qǐng)求,允許突發(fā) 2 個(gè)請(qǐng)求
limiter := rate.NewLimiter(1, 2)
http.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) {
if limiter.Allow() {
fmt.Fprintln(w, "Request allowed")
} else {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
}
})
fmt.Println("Server started at :8080")
_ = http.ListenAndServe(":8080", nil)
}
正常請(qǐng)求:

頻繁請(qǐng)求(超過(guò)頻次):

模擬等待耗時(shí)任務(wù)處理
可用于限制資源(文件)的訪(fǎng)問(wèn),避免資源(文件)被頻繁訪(fǎng)問(wèn)造成性能問(wèn)題。
func Demo_HandleWorkWithWait() {
// 創(chuàng)建一個(gè)限速器,每3秒允許1個(gè)事件
limiter := rate.NewLimiter(rate.Every(3*time.Second), 1)
// 模擬10次對(duì)資源的訪(fǎng)問(wèn)
for i := 0; i < 10; i++ {
// 使用limiter.Wait(ctx)等待,直到可以訪(fǎng)問(wèn)資源(訪(fǎng)問(wèn)文件/執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)等)
if err := limiter.Wait(context.Background()); err != nil {
log.Fatalf("Failed to wait for rate limiter: %v", err)
}
// 訪(fǎng)問(wèn)資源
fmt.Printf("Accessing resource at %v\n", time.Now())
}
}
全部代碼
Github:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-rat
參考文章:
https://cloud.tencent.com/developer/article/2429254
https://www.cnblogs.com/gnivor/p/10623028.html
到此這篇關(guān)于go rate 原生標(biāo)準(zhǔn)限速庫(kù)的使用的文章就介紹到這了,更多相關(guān)go rate 原生標(biāo)準(zhǔn)限速庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言的IO庫(kù)那么多糾結(jié)該如何選擇
在Go語(yǔ)言中涉及 I/O 操作的內(nèi)置庫(kù)有很多種,比如: io 庫(kù), os 庫(kù), ioutil 庫(kù), bufio 庫(kù), bytes 庫(kù), strings 庫(kù)等等。擁有這么多內(nèi)置庫(kù)是好事,但是具體到涉及 I/O 的場(chǎng)景我們應(yīng)該選擇哪個(gè)庫(kù)呢,帶著這個(gè)問(wèn)題一起通過(guò)本文學(xué)習(xí)下吧2021-06-06
詳解Gotorch多機(jī)定時(shí)任務(wù)管理系統(tǒng)
遵循著“學(xué)一門(mén)語(yǔ)言最好的方式是使用它”的理念,想著用Go來(lái)實(shí)現(xiàn)些什么,剛好有一個(gè)比較讓我煩惱的問(wèn)題,于是用Go解決一下,即使不在生產(chǎn)環(huán)境使用,也可以作為Go語(yǔ)言學(xué)習(xí)的一種方式。2021-05-05
Go語(yǔ)言用map實(shí)現(xiàn)堆棧功能的方法
這篇文章主要介紹了Go語(yǔ)言用map實(shí)現(xiàn)堆棧功能的方法,實(shí)例分析了Go語(yǔ)言使用map操作堆棧的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
go本地環(huán)境配置及vscode go插件安裝的詳細(xì)教程
這篇文章主要介紹了go本地環(huán)境配置及vscode go插件安裝的詳細(xì)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
關(guān)于Golang的Map的線(xiàn)程安全問(wèn)題的解決方案
在?Golang?編程中,map?是一種常用的數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)鍵值對(duì),然而,Golang?的?map?在并發(fā)訪(fǎng)問(wèn)時(shí)是線(xiàn)程不安全的,本文將詳細(xì)介紹?Golang?中?map?的線(xiàn)程不安全性,并提供一些解決方案,幫助開(kāi)發(fā)者在并發(fā)編程中正確使用?map,需要的朋友可以參考下2024-11-11
golang 數(shù)組去重,利用map的實(shí)現(xiàn)
這篇文章主要介紹了golang 數(shù)組去重,利用map的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04

