トークンバケットに基づく電流制限アルゴリズム
- r: 毎秒 r 個のトークンをバケットに入れます。つまり、1/r 秒ごとにトークンを入れます。
- b: バケットの最大容量は b であり、バケットがいっぱいになった後に追加しようとしたトークンは破棄されます。
- 誰かが n 個のトークンをリクエストしたとき、バケット内のトークンの数が n 未満であれば、リクエストはブロックされるか直接諦められます。それ以外の場合は、バケットから n 個のトークンが正常に取得されます。
- b > 1 の場合、1/r 秒以内に最大 b 個のトークンを取り除くことができます。
- 同時実行の最大量を非常に短い瞬間に制限します。b = 同時実行の最大量、r = 最短の期間
- b = 1 の場合、1 秒あたり最大 r 個のトークンを奪うことができます
- 1 秒あたりの最高 QPS を制限します: b=1、r= 最高 QPS
- 1 分あたりの最大リクエスト数を制限する: 1 分あたりのリクエスト数 / 60= を 1 秒に変換し、それを r に割り当てます。
- 5分ごと、10分ごとに制限する、同じ理由
- b > 1 の場合、1/r 秒以内に最大 b 個のトークンを取り除くことができます。
高同時実行電流制限を実装します (golang 公式電流制限機能を使用)
Goコード
ミドルウェア/rateLimiterMiddleware.go
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
var Limiter *rate.Limiter
// 定义一个中间件函数来进行限流
func RateLimiterMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if !Limiter.AllowN(time.Now(), 1) {
c.JSON(http.StatusTooManyRequests, gin.H{
"message": "Rate limit exceeded"})
// 设置休眠和业务时长一样,为了更好从日志出看出规则
time.Sleep(50 * time.Millisecond)
c.Abort()
return
}
c.Next()
}
}
メイン.ゴー
func main() {
r := gin.Default()
// 创建一个限流器,每秒允许最多10个请求
middleware.Limiter = rate.NewLimiter(rate.Limit(10), 1)
// 使用限流中间件
r.Use(middleware.RateLimiterMiddleware())
r.GET("/api/resource", func(c *gin.Context) {
time.Sleep(50 * time.Millisecond)
c.JSON(http.StatusOK, gin.H{
"message": "Resource accessed"})
})
r.Run(":8080")
}
試験記録
ab -t 1 -c 1 http://127.0.0.1:8080/api/resource
ab ストレステストを使用すると、同時実行性は 1 (シリアルと同等)、リクエストは 1 秒以内に連続して発行されます (1 リクエストあたり 50ms、合計 20 リクエストを発行できると計算)
結果の予測: 1 秒間に最大 10 個のトークンが生成され、合計 20 個のシリアル リクエストがあります。結果は 1 回の成功 (50 ミリ秒で終了) と 1 回の失敗 (次の 50 ミリ秒で新しいトークンは生成されません) になるはずです。 . )、1 つは成功し、1 つは失敗しました。。。
結果の出力 (期待通り)
アップグレード: 各 IP アドレスに基づいてトラフィックを制限する
Goコード
ミドルウェア/ipRateLimiterMiddleware.go
package middleware
import (
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
var IPLimiter *IPRateLimiter
func NewIPRateLimiter() *IPRateLimiter {
return &IPRateLimiter{
limiter: make(map[string]*rate.Limiter),
}
}
type IPRateLimiter struct {
mu sync.Mutex
limiter map[string]*rate.Limiter
}
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
i.mu.Lock()
defer i.mu.Unlock()
limiter, exists := i.limiter[ip]
if !exists {
limiter = rate.NewLimiter(2, 5) // 每秒2个请求,桶容量为5
i.limiter[ip] = limiter
}
return limiter
}
// 定义一个中间件函数来进行限流
func IPRateLimiterMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
ip := c.ClientIP()
limiter := IPLimiter.GetLimiter(ip)
if !limiter.Allow() {
c.JSON(http.StatusTooManyRequests, gin.H{
"message": "Rate limit exceeded"})
// 设置休眠和业务时长一样,为了更好从日志出看出规则
time.Sleep(50 * time.Millisecond)
c.Abort()
return
}
c.Next()
}
}
メイン.ゴー
func main() {
r := gin.Default()
// 创建IP限流器
middleware.IPLimiter = middleware.NewIPRateLimiter()
// 使用限流中间件
r.Use(middleware.IPRateLimiterMiddleware())
r.GET("/api/resource", func(c *gin.Context) {
time.Sleep(50 * time.Millisecond)
c.JSON(http.StatusOK, gin.H{
"message": "Resource accessed"})
})
r.Run(":8080")
}
試験記録
ab -t 1 -c 1 http://127.0.0.1:8080/api/resource
ab ストレステストを使用すると、同時実行性は 1 (シリアルと同等)、リクエストは 1 秒以内に連続して発行されます (1 リクエストあたり 50ms、合計 20 リクエストを発行できると計算)
結果の予測: 1 秒間に最大 2 つのトークンが生成され、バケットの容量は 5 です。これは、1/2 秒以内の最大同時実行数が 5 であることを意味します。合計 20 のシリアル リクエストがあります。結果は次のようになります。最初に 5 つが成功し (バケット容量はすべて正常に使用されました)、その後、残りの 1 つが成功し、残りはすべて失敗しました。
- プログラムが開始されると、電流リミッターが初期化され、バケットは空でトークンがありません。
- 1 秒あたり最初の 2 つのトークン生成では、毎回 2 つのトークンが生成され、バケットに入れられます。
- 3 秒目では、バケットがすでにいっぱいであるため、生成された 2 つのトークンのうち 1 つだけをバケットに入れることができます。
- ab を使用してリクエストを行うと、最初の 5 つのリクエストが次々にトークンを取得して成功し、各リクエストに 50 ミリ秒かかりました。この時点でかかる合計時間は 250ms です。
- 6 番目のリクエストは 300 ミリ秒、7 番目のリクエストは 350 ミリ秒、8 番目のリクエストは 400 ミリ秒、9 番目のリクエストは 450 ミリ秒、10 番目のリクエストは 500 ミリ秒です。
- 電流リミッターのレートは 1 秒あたり 2 リクエストであるため、つまり、トークンは 500 ミリ秒ごとに生成されます。新しいトークンは 500 ミリ秒で生成されるため、6 ~ 10 番目のリクエストではトークンがないためすべてのリクエストが失敗しますが、11 番目のリクエストでは新しいトークンが生成されるため成功します。
- 後続のリクエストは、新しいトークンが生成されるまで待機します。
結果の出力 (期待通り)