Go 分散トークン バケットの電流制限 + 最終利益の保証

Go 分散トークン バケットの電流制限 + 最終利益の保証

この記事は、Ouyang An が執筆した WeChat パブリックアカウント「Microservice Practice」から転載したものです。この記事を転載する場合は、Microservice Practice のパブリック アカウントにお問い合わせください。

前の記事では、固定時間ウィンドウの電流制限では突然の要求のピークに対応できないことを説明しました。この記事で説明するトークン バケット回路アルゴリズムは、このシナリオをより適切に処理できます。

仕組み

単位時間あたりに生成されるトークンは、バケット容量の上限に達するまで一定の割合でバケットに入れられます。

リクエストを処理します。そのたびに 1 つ以上のトークンを取得しようとします。取得できた場合はリクエストを処理し、取得できなかった場合はリクエストを拒否します。

長所と短所

アドバンテージ

瞬間的なバーストトラフィックを効果的に処理でき、バケット内のトークンをトラフィックバッファとして使用してバーストトラフィックをスムーズに処理できます。

欠点

実装はより複雑です。

コードの実装

  1. コア/制限/トークン制限.go

分散環境でバケットとトークンのストレージ コンテナーとして Redis を使用することを検討し、Lua スクリプトを使用してアルゴリズム プロセス全体を実装します。

Redis Lua スクリプト

  1. -- 1秒あたりに生成されるトークンの数、つまりトークン生成速度 
  2. ローカルレート = tonumber(ARGV[1])
  3. -- バケット容量 
  4. ローカル容量 = tonumber(ARGV[2])
  5. -- 現在のタイムスタンプ 
  6. ローカルnow = tonumber(ARGV[3])
  7. -- 現在のリクエストトークン番号 
  8. ローカル要求 = tonumber(ARGV[4])
  9. -- バケツを満たすのに何秒かかりますか 
  10. ローカルfill_time = 容量/レート
  11. -- 切り捨て、ttl は fill 時間の 2 倍になります 
  12. ローカルttl = math.floor(fill_time*2)
  13. -- 現在のタイムバケット容量 
  14. ローカルlast_tokens = tonumber(redis.call( "get" , KEYS[1]))
  15. -- 現在のバケット容量が0の場合、初めて入力することを意味し、デフォルトの容量はバケットの最大容量です。  
  16. last_tokens == nilの場合 
  17. last_tokens = 容量
  18. 終わり 
  19. -- 最終更新時刻 
  20. ローカルlast_refreshed = tonumber(redis.call( "get" , KEYS[2]))
  21. -- 最初のエントリの更新時間を 0 に設定します 
  22. last_refreshed == nilの場合 
  23. 最終更新日 = 0
  24. 終わり 
  25. -- 最後のリクエストからの経過時間 
  26. ローカルデルタ = 数学。最大(0, 現在 - 最終更新日)
  27. -- 最後のリクエストからの時間間隔、生成できるトークンの総数、最大容量を超えた場合、超過したトークンは破棄されます 
  28. ローカルfilled_tokens = math.最小(容量、last_tokens+(delta*rate))
  29. -- このリクエストのトークンの数は十分ですか?  
  30. ローカルで許可 = filled_tokens >= 要求
  31. -- 残りのバケット数 
  32. ローカルnew_tokens = filled_tokens
  33. -- このトークンの適用を許可し、残りの数量を計算します 
  34. 許可されれば 
  35. new_tokens = filled_tokens - リクエスト済み
  36. 終わり 
  37. -- 残りのトークンの数を設定する 
  38. redis.call( "setex" , KEYS[1], ttl, new_tokens)
  39. -- 更新時間を設定する 
  40. redis.call( "setex" , KEYS[2], ttl, now)
  41.  
  42. 返品可能

トークン バケット リミッターの定義

  1. TokenLimiter構造体型{
  2. // 1秒あたりの生産率
  3. レートint  
  4. // バケット容量
  5. バースト整数 
  6. // ストレージコンテナ
  7. *redis.Redis を保存します。
  8. // Redisキー 
  9. トークンキー文字列
  10. // バケット更新時間キー 
  11. タイムスタンプキー文字列
  12. // ロック
  13. rescueLock sync.Mutex
  14. // Redis ヘルスフラグ
  15. redisAlive uint32
  16. // Redis が失敗した場合は、インプロセス トークン バケットの現在のリミッターを使用します
  17. レスキューリミッター *xrate.リミッター
  18. // Redis 監視検出タスク識別子
  19. monitorStarted ブール値
  20. }
  21.  
  22. func NewTokenLimiter(rate, burst int , store *redis.Redis, key string) *TokenLimiter {
  23. tokenKey := fmt.Sprintf(tokenFormat, key )
  24. timestampKey := fmt.Sprintf(timestampFormat, key )
  25.  
  26. &TokenLimiter{を返す
  27. レート: レート、
  28. バースト:バースト、
  29. ストア: ストア、
  30. トークンキー: トークンキー、
  31. タイムスタンプキー: タイムスタンプキー、
  32. ライブ: 1,
  33. レスキューリミッター: xrate.NewLimiter(xrate.Every( time . Second / time .Duration(rate)), burst),
  34. }
  35. }

トークンを取得する

  1. func (lim *TokenLimiter) reserveN(now time . Time , n int ) bool {
  2. // Redis が正常かどうかを判定する
  3. // Redis が失敗したときにプロセス内電流制限を使用する
  4. // ボトムライン保護
  5. atomic.LoadUint32(&lim.redisAlive) == 0 の場合 {
  6. lim.rescueLimiter.AllowN(now, n)を返します
  7. }
  8. // スクリプトを実行してトークンを取得します
  9. 応答、err := lim.store.Eval(
  10. スクリプト、
  11. []弦{
  12. limit.tokenKey、
  13. lim.timestampKey、
  14. },
  15. []弦{
  16. strconv.Itoa(制限率)、
  17. strconv.Itoa(limit.burst)、
  18. strconv.FormatInt(now.Unix(), 10)、
  19. strconv.Itoa(n)、
  20. })
  21. // redis 許可 == false  
  22. // Lua ブール値false -> r Nil 一括返信
  23. //キーが存在しないケースの特別な処理
  24. err == redis.Nilの場合{
  25. 戻る 間違い 
  26. }そうでなければ err != nil {
  27. logx.Errorf( "レートリミッターの使用に失敗しました: %s、レスキューにはインプロセスリミッターを使用してください" , ​​err)
  28. // 実行例外、Redis ヘルス検出タスクを開始
  29. // フォールバックとしてインプロセス電流リミッターも使用する
  30. lim.startMonitor()
  31. lim.rescueLimiter.AllowN(now, n)を返します
  32. }
  33.  
  34. コード、ok := resp.(int64)
  35. !okの場合{
  36. logx.Errorf( "redis スクリプトの評価に失敗しました: %v、レスキューにはインプロセス リミッターを使用してください" , ​​resp)
  37. lim.startMonitor()
  38. lim.rescueLimiter.AllowN(now, n)を返します
  39. }
  40.  
  41. // redis 許可 == true  
  42. // Lua ブール値true -> r整数応答1
  43. 戻りコード == 1
  44. }

Redis 障害フォールバック戦略

バックアップ戦略の設計は非常に詳細です。 redis が利用できない場合、基本的なフロー制限が利用可能であり、サービスが過負荷にならないようにするために、ratelimit のスタンドアロン バージョンがバックアップ フロー制限として起動されます。

  1. // Redis のヘルス検出を有効にする
  2. func (lim *TokenLimiter) startMonitor() {
  3. lim.rescueLock.Lock()
  4. lim.rescueLock.Unlock() を延期する
  5. // 繰り返し開くのを防ぐ
  6. lim.monitorStartedの場合{
  7. 戻る 
  8. }
  9.  
  10. // タスクとヘルスフラグを設定する
  11. lim.monitorStarted = true  
  12. アトミック.StoreUint32(&lim.redisAlive, 0)
  13. // ヘルス検出
  14. lim.waitForRedis() を実行する
  15. }
  16.  
  17. // Redis ヘルス検出スケジュールタスク
  18. func (lim *TokenLimiter) waitForRedis() {
  19. ティッカー:=時間.NewTicker(pingInterval)
  20. // この関数はヘルス検出が成功したときにコールバックされます
  21. 遅延関数() {
  22. ticker.Stop()
  23. lim.rescueLock.Lock()
  24. lim.monitorStarted = false  
  25. lim.rescueLock.Unlock()
  26. }()
  27.  
  28. 範囲ticker.C {
  29. // pingはRedisの組み込みヘルス検出コマンドです
  30. lim.store.Ping() の場合 {
  31. //ヘルス検出が成功したので、ヘルスフラグを設定します
  32. アトミック.StoreUint32(&limit.redisAlive, 1)
  33. 戻る 
  34. }
  35. }
  36. }

プロジェクトギャラリー

https://github.com/zeromicro/go-zero

go-zero をご利用いただき、ぜひスターを付けてください!

<<:  クラウド コンピューティングがビジネスを成功に導く 4 つの方法

>>:  2022年に注目すべき5つのクラウドトレンド

推薦する

hostodo-アジアに最適化された VPS/KVM/$4.5/1g メモリ/30g ハードディスク/1.5T トラフィック

hostodo からの公式メールには、特別な低価格の VPS、KVM、クアドラネット コンピュータ ...

Oracle Analytics Cloudが多くの新しいイノベーションをリリース

自動化された分析ワークフローは、企業がより多様な方法でデータを操作して予測を行うのに役立ちます。 O...

電子商取引事業を始めて10年近く経ちますが、まだ利益が出ていないというのは本当でしょうか?

8月中旬、電子商取引で儲けることの是非をめぐる議論が、一般的なB2C/C2B/C2Cの概念を覆すもの...

ニュース: Linode/第 13 回南カリフォルニア Linux エキスポが 40% オフ

linode.comより最新の公式ニュースによると、Linode はロサンゼルスで開催される第 13...

locvps: "618"、30% 割引、618 をトップアップすると 150 がもらえます、VPS オプション: 香港 cn2\ドイツ cn2\米国 cn2

国産VPSブランドlocvpsの618プロモーションは以下のとおりです:(1)指定KVMシリーズVP...

デジタル変革2.0の時代に、ファーウェイクラウドアプリケーションプラットフォームROMAはデジタル変革の「ローマの道」を築く

デジタル経済は経済成長を推進する重要な原動力として、大きな変革の時代を迎えており、あらゆる業界や分野...

Weiboマーケティング:フェイスプロジェクトで良い仕事をする方法

古代中国の四大美人は、いずれもその美しさと内面の素質が称賛されています。私が彼女を最初に発見したのは...

5Gがクラウドネットワークの統合を加速

クラウドコンピューティングとは何ですか?企業によって定義が全く異なります。米国国立標準技術研究所は、...

#黒5# hosteons: すべての VPS が 30% オフ、200Mbps 無制限、Windows が無料

Hosteons のブラックフライデー プロモーションが開始され、すべての VPS が 30% オフ...

11のグループの関係により、JVMを明確に把握できます。

[[358883]]では、早速本題に入りましょう。グループ 1: JDK、JRE、JVM の関係JD...

SEO ピア競争の秘密兵器を解き放つ 4 つの鍵

SEO 運用モデルはビジネスモデルであり、今日の社会における数千万のビジネス競争の 1 つです。した...

SEO担当者がビジネスウェブサイトを運営する際に考慮すべき2つの問題

時代の進歩に伴い、SEOを重視する中小企業が増えているため、現在国内の人材需要は依然として非常に大き...

コンテンツページの最適化を通じてウェブサイトのランキングを向上させる方法

「外部リンクこそが王様」という言葉は、SEO 従事者が常に主張してきた言葉ですが、外部リンクがあらゆ...

ネットセレブブランドマーケティング 0から1へ

Z世代が徐々に消費の場に進出し始めるにつれて、多様化、個性化、カスタマイズ化した消費者のニーズが、一...

ウェブマスターの共有: 長年にわたる SEO の旅について語る

年末の総括と言えば、私は基本的に書いたことがありません。前年の年末に書いたログは、細かいことばかりで...