go rate 原生標(biāo)準(zhǔn)限速庫(kù)的使用
在我們實(shí)際生產(chǎn)開發(fā)過程中,不免存在一些高并發(fā)的場(chǎng)景,此時(shí)我們就需要對(duì)請(qǐng)求進(jìn)行限流,避免過高的QPS,影響我們服務(wù)器,導(dǎo)致服務(wù)出現(xiàn)波動(dòng)或不可用。
- 對(duì)于go開發(fā)人員而言,golang標(biāo)準(zhǔn)庫(kù)就已經(jīng)為我們提供了現(xiàn)成的類庫(kù)
golang.org/x/time/rate
,我們只需直接調(diào)用即可。下面將為大家介紹該類庫(kù)的詳細(xì)用法。
文中代碼地址,歡迎大家??:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-rate
介紹
限流(Rate Limiting)是控制對(duì)某些資源訪問頻率的一種技術(shù)手段。在高并發(fā)的服務(wù)中,限流機(jī)制可以有效防止資源過載、服務(wù)崩潰,保障系統(tǒng)的穩(wěn)定性和可用性。Golang 官方標(biāo)準(zhǔn)庫(kù) golang.org/x/time/rate 提供了一個(gè)高效且易用的限流器(Rate Limiter),可以幫助開發(fā)者方便地實(shí)現(xiàn)限流功能。
原理:golang官方的類庫(kù)采用了令牌桶算法進(jìn)行限流。
- 令牌桶算法(Token Bucket Algorithm):是一種常用的限流算法,它通過在固定時(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)建限流器后,可以通過 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)我們通過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)求(超過頻次):
模擬等待耗時(shí)任務(wù)處理
可用于限制資源(文件)的訪問,避免資源(文件)被頻繁訪問造成性能問題。
func Demo_HandleWorkWithWait() { // 創(chuàng)建一個(gè)限速器,每3秒允許1個(gè)事件 limiter := rate.NewLimiter(rate.Every(3*time.Second), 1) // 模擬10次對(duì)資源的訪問 for i := 0; i < 10; i++ { // 使用limiter.Wait(ctx)等待,直到可以訪問資源(訪問文件/執(zhí)行數(shù)據(jù)庫(kù)查詢等) if err := limiter.Wait(context.Background()); err != nil { log.Fatalf("Failed to wait for rate limiter: %v", err) } // 訪問資源 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ǔ)言下載網(wǎng)絡(luò)圖片或文件的方法示例
這篇文章主要介紹了Go語(yǔ)言下載網(wǎng)絡(luò)圖片或文件的方法示例,文中通過示例代碼介紹的非常詳細(xì),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-12-12Golang信號(hào)量設(shè)計(jì)實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Golang信號(hào)量設(shè)計(jì)實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)
HTTP協(xié)議是基于TCP/IP協(xié)議棧的,并且它也是一個(gè)面向普通文本的協(xié)議。這篇文章主要詳細(xì)介紹了Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù),感興趣的小伙伴可以借鑒一下2023-04-04深入了解Golang中的Slice底層實(shí)現(xiàn)
本文主要為大家詳細(xì)介紹了Golang中slice的底層實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2023-02-02Golang中匿名組合實(shí)現(xiàn)偽繼承的方法
這篇文章主要介紹了Golang中匿名組合實(shí)現(xiàn)偽繼承的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-08-08