Golang はカスタム IP 電流制限ミドルウェアを作成します

トークンバケットに基づく電流制限アルゴリズム

  • 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分ごとに制限する、同じ理由

高同時実行電流制限を実装します (golang 公式電流制限機能を使用)

Goコード

ソースコードアドレス:GitHub-golangバージョン

ミドルウェア/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コード

ソースコードアドレス:GitHub-golangバージョン

ミドルウェア/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. 1 秒あたり最初の 2 つのトークン生成では、毎回 2 つのトークンが生成され、バケットに入れられます。
  3. 3 秒目では、バケットがすでにいっぱいであるため、生成された 2 つのトークンのうち 1 つだけをバケットに入れることができます。
  4. ab を使用してリクエストを行うと、最初の 5 つのリクエストが次々にトークンを取得して成功し、各リクエストに 50 ミリ秒かかりました。この時点でかかる合計時間は 250ms です。
  5. 6 番目のリクエストは 300 ミリ秒、7 番目のリクエストは 350 ミリ秒、8 番目のリクエストは 400 ミリ秒、9 番目のリクエストは 450 ミリ秒、10 番目のリクエストは 500 ミリ秒です。
  6. 電流リミッターのレートは 1 秒あたり 2 リクエストであるため、つまり、トークンは 500 ミリ秒ごとに生成されます。新しいトークンは 500 ミリ秒で生成されるため、6 ~ 10 番目のリクエストではトークンがないためすべてのリクエストが失敗しますが、11 番目のリクエストでは新しいトークンが生成されるため成功します。
  7. 後続のリクエストは、新しいトークンが生成されるまで待機します。

結果の出力 (期待通り)

ここに画像の説明を挿入します
ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/trinityleo5/article/details/132889703