Go語言如何實(shí)現(xiàn)限制用戶請(qǐng)求
在 Go 語言中,限制用戶每分鐘最多請(qǐng)求 1000 次的常見做法是使用 限流算法(Rate Limiting)。有多種算法可以實(shí)現(xiàn)這一目標(biāo),其中最常見的包括 令牌桶算法 (Token Bucket)、漏桶算法 (Leaky Bucket) 和 計(jì)數(shù)器算法 (Counter)。每種算法有其特點(diǎn)和適用場(chǎng)景,下面將逐個(gè)介紹,并附上相應(yīng)的 Go 語言實(shí)現(xiàn)。
1. 令牌桶算法 (Token Bucket)
令牌桶算法是常見的限流算法,適用于需要平滑流量控制的場(chǎng)景。令牌桶維護(hù)一個(gè)存儲(chǔ)令牌的桶,每個(gè)請(qǐng)求需要消耗一個(gè)令牌。如果桶內(nèi)有足夠的令牌,請(qǐng)求可以繼續(xù);如果沒有令牌,則請(qǐng)求被拒絕。令牌按固定速率生成,當(dāng)桶滿時(shí),額外的令牌會(huì)丟棄。
令牌桶算法的實(shí)現(xiàn)
package main
import (
"fmt"
"sync"
"time"
)
type TokenBucket struct {
rate int // 生成令牌的速率,單位是令牌/秒
capacity int // 桶的容量
tokens int // 當(dāng)前令牌數(shù)量
lastToken time.Time // 上次生成令牌的時(shí)間
mutex sync.Mutex // 用于并發(fā)控制
}
func NewTokenBucket(rate, capacity int) *TokenBucket {
return &TokenBucket{
rate: rate,
capacity: capacity,
tokens: capacity, // 初始時(shí),桶里有滿的令牌
}
}
func (tb *TokenBucket) refill() {
// 計(jì)算過去時(shí)間段內(nèi)生成的令牌數(shù)
now := time.Now()
elapsed := now.Sub(tb.lastToken)
tb.lastToken = now
// 按速率生成令牌
newTokens := int(elapsed.Seconds()) * tb.rate
if newTokens > 0 {
// 桶中令牌數(shù)增加
tb.tokens += newTokens
if tb.tokens > tb.capacity {
// 超過桶容量,令牌數(shù)只能是桶的最大容量
tb.tokens = tb.capacity
}
}
}
func (tb *TokenBucket) Allow() bool {
tb.mutex.Lock()
defer tb.mutex.Unlock()
// 補(bǔ)充令牌
tb.refill()
if tb.tokens > 0 {
// 有令牌可以消耗
tb.tokens--
return true
}
// 沒有令牌可用,限制請(qǐng)求
return false
}
func main() {
// 創(chuàng)建令牌桶,令牌生成速率為每秒 1000 個(gè),容量為 1000 個(gè)令牌
tb := NewTokenBucket(1000, 1000)
// 模擬用戶發(fā)起請(qǐng)求
for i := 0; i < 10; i++ {
if tb.Allow() {
fmt.Println("Request", i+1, "allowed")
} else {
fmt.Println("Request", i+1, "rejected")
}
time.Sleep(100 * time.Millisecond) // 模擬請(qǐng)求間隔
}
}
說明:
rate:每秒生成的令牌數(shù)。
capacity:桶的最大容量。
tokens:當(dāng)前桶中可用的令牌數(shù)。
每次請(qǐng)求時(shí),Allow() 方法會(huì)檢查桶中是否有令牌,如果有,則消耗一個(gè)令牌并允許請(qǐng)求;如果沒有令牌,則拒絕請(qǐng)求。
2. 漏桶算法 (Leaky Bucket)
漏桶算法是另一種常用的限流算法,適用于流量平滑控制。在漏桶算法中,桶里有水(請(qǐng)求),水按固定速率流出。當(dāng)請(qǐng)求到來時(shí),如果桶滿了,新的請(qǐng)求會(huì)被丟棄;如果桶未滿,新的請(qǐng)求會(huì)被加入桶中,并在固定速率下流出。
漏桶算法的實(shí)現(xiàn)
package main
import (
"fmt"
"sync"
"time"
)
type LeakyBucket struct {
rate int // 水流出速率,單位是請(qǐng)求/秒
capacity int // 桶的容量
water int // 當(dāng)前桶中水的數(shù)量
lastDrain time.Time // 上次排水時(shí)間
mutex sync.Mutex // 用于并發(fā)控制
}
func NewLeakyBucket(rate, capacity int) *LeakyBucket {
return &LeakyBucket{
rate: rate,
capacity: capacity,
water: 0, // 初始時(shí),桶里沒有水
}
}
func (lb *LeakyBucket) drain() {
// 計(jì)算過去時(shí)間段內(nèi)排出的請(qǐng)求數(shù)
now := time.Now()
elapsed := now.Sub(lb.lastDrain)
lb.lastDrain = now
// 按排出速率流出請(qǐng)求
drained := int(elapsed.Seconds()) * lb.rate
if drained > 0 {
lb.water -= drained
if lb.water < 0 {
lb.water = 0
}
}
}
func (lb *LeakyBucket) Allow() bool {
lb.mutex.Lock()
defer lb.mutex.Unlock()
// 排水
lb.drain()
if lb.water < lb.capacity {
// 桶未滿,允許請(qǐng)求
lb.water++
return true
}
// 桶已滿,拒絕請(qǐng)求
return false
}
func main() {
// 創(chuàng)建漏桶,排水速率為每秒 1000 個(gè),桶的容量為 1000 個(gè)
lb := NewLeakyBucket(1000, 1000)
// 模擬用戶發(fā)起請(qǐng)求
for i := 0; i < 10; i++ {
if lb.Allow() {
fmt.Println("Request", i+1, "allowed")
} else {
fmt.Println("Request", i+1, "rejected")
}
time.Sleep(100 * time.Millisecond) // 模擬請(qǐng)求間隔
}
}
說明:
rate:請(qǐng)求的排出速率。
capacity:桶的最大容量。
water:當(dāng)前桶中水(請(qǐng)求)的數(shù)量。
drain():排水操作,控制請(qǐng)求的流出速率。
3. 計(jì)數(shù)器算法 (Fixed Window Counter)
計(jì)數(shù)器算法是最簡(jiǎn)單的一種限流算法。在每個(gè)時(shí)間窗口內(nèi),記錄請(qǐng)求的數(shù)量。當(dāng)請(qǐng)求數(shù)達(dá)到限制時(shí),就會(huì)拒絕進(jìn)一步的請(qǐng)求。它適用于簡(jiǎn)單的限流場(chǎng)景,但對(duì)于高并發(fā)時(shí)可能會(huì)出現(xiàn)窗口突發(fā)的情況。
計(jì)數(shù)器算法的實(shí)現(xiàn)
package main
import (
"fmt"
"sync"
"time"
)
type Counter struct {
limit int // 請(qǐng)求限制次數(shù)
windowSize time.Duration // 時(shí)間窗口大小
mu sync.Mutex // 用于并發(fā)控制
requests int // 當(dāng)前請(qǐng)求計(jì)數(shù)
windowStart time.Time // 當(dāng)前時(shí)間窗口開始時(shí)間
}
func NewCounter(limit int, windowSize time.Duration) *Counter {
return &Counter{
limit: limit,
windowSize: windowSize,
requests: 0,
windowStart: time.Now(),
}
}
func (c *Counter) Allow() bool {
c.mu.Lock()
defer c.mu.Unlock()
// 判斷是否在當(dāng)前時(shí)間窗口內(nèi)
now := time.Now()
if now.Sub(c.windowStart) > c.windowSize {
// 如果超過了窗口時(shí)間,則重置請(qǐng)求計(jì)數(shù)器和窗口開始時(shí)間
c.windowStart = now
c.requests = 0
}
if c.requests < c.limit {
// 如果請(qǐng)求數(shù)未達(dá)到限制,允許請(qǐng)求
c.requests++
return true
}
// 否則,拒絕請(qǐng)求
return false
}
func main() {
// 創(chuàng)建計(jì)數(shù)器,限制每分鐘 1000 次請(qǐng)求
counter := NewCounter(1000, time.Minute)
// 模擬用戶發(fā)起請(qǐng)求
for i := 0; i < 10; i++ {
if counter.Allow() {
fmt.Println("Request", i+1, "allowed")
} else {
fmt.Println("Request", i+1, "rejected")
}
time.Sleep(100 * time.Millisecond) // 模擬請(qǐng)求間隔
}
}
說明:
limit:時(shí)間窗口內(nèi)允許的最大請(qǐng)求次數(shù)。
windowSize:時(shí)間窗口的大小(比如 1 分鐘)。
requests:當(dāng)前時(shí)間窗口內(nèi)已處理的請(qǐng)求數(shù)量。
Allow():每次請(qǐng)求時(shí),檢查當(dāng)前窗口內(nèi)請(qǐng)求數(shù)是否達(dá)到限制。
4. 總結(jié)
令牌桶算法(Token Bucket)適用于平滑流量控制,允許突發(fā)請(qǐng)求。
漏桶算法(Leaky Bucket)適用于平滑流量,適合流量控制比較嚴(yán)格的場(chǎng)景。
計(jì)數(shù)器算法(Counter)是最簡(jiǎn)單的一種限流方式,適合簡(jiǎn)單的限流需求,但對(duì)突發(fā)流量處理較差。根據(jù)不同的需求場(chǎng)景,選擇合適的算法進(jìn)行實(shí)現(xiàn)。
到此這篇關(guān)于Go語言如何實(shí)現(xiàn)限制用戶請(qǐng)求的文章就介紹到這了,更多相關(guān)Go限制用戶請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go?gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP
這篇文章主要為大家介紹了Go?gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Go語言web快速開發(fā)框架Gin的HttpRouter路由的使用
in框架內(nèi)部使用了高性能的路由器庫(kù)httprouter,支持動(dòng)態(tài)參數(shù)匹配和簡(jiǎn)潔的接口,本文主要介紹了Go語言web快速開發(fā)框架Gin的HttpRouter路由的使用,感興趣的可以了解一下2025-03-03
Go語言基礎(chǔ)學(xué)習(xí)之Context的使用詳解
在Go語言中,Context是一個(gè)非常重要的概念,它用于在不同的?goroutine?之間傳遞請(qǐng)求域的相關(guān)數(shù)據(jù),本文將深入探討Go語言中?Context特性和Context的高級(jí)使用方法,希望對(duì)大家有所幫助2023-05-05
基于HLS創(chuàng)建Golang視頻流服務(wù)器的優(yōu)缺點(diǎn)
HLS 是 HTTP Live Streaming 的縮寫,是蘋果開發(fā)的一種基于 HTTP 的自適應(yīng)比特率流媒體傳輸協(xié)議。這篇文章主要介紹了基于 HLS 創(chuàng)建 Golang 視頻流服務(wù)器,需要的朋友可以參考下2021-08-08
詳解Golang如何優(yōu)雅的終止一個(gè)服務(wù)
后端服務(wù)通常會(huì)需要?jiǎng)?chuàng)建子協(xié)程來進(jìn)行相應(yīng)的作業(yè),但進(jìn)程接受到終止信號(hào)或正常結(jié)束時(shí),并沒有判斷或等待子協(xié)程執(zhí)行結(jié)束,下面這篇文章主要給大家介紹了關(guān)于Golang如何優(yōu)雅的終止一個(gè)服務(wù)的相關(guān)資料,需要的朋友可以參考下2022-03-03

