レート制限:トークンバケットと漏れバケットの実際の違い

トークンバケットは突発を許し、漏れバケットは一定速度に均します。選び間違えると、正常な利用者が制限されたり、制限が機能しなかったりします。

二つの古典的な模型は同義語のように扱われますが、出力の形がまったく違います。一方は突発を保ち、他方は消します。

トークンバケット:突発を許す

容量 C のバケットに一定速度でトークンが溜まります。要求はトークンを一つ取り、無ければ制限されます。

  • 速度 R が長期的な平均スループット
  • 容量 C が許容する突発の大きさ

アイドル後はバケットが満杯なので、C 件が瞬時に通ります。ページが一度に 20 件要求するような正常な挙動にはこれが必要です。 漏れバケットはそれを平準化し、利用者には一件ずつ読まれるのが見えます。

漏れバケット:一定の出力

要求は待ち行列に入り、一定速度で出ます。満杯なら破棄されます。

出力は一定で、代償は遅延の追加です。要求は待ち行列で待ちます。下流の保護は強いが、体験は悪化します。

比較

観点 トークンバケット 漏れバケット
出力速度 可変、最大 C 一定
突発 許す 許さない
遅延 加えない 加える
守る対象 余力のあるサービス 脆い下流

どちらを選ぶか

問いは一つです。下流は突発を消化できるか。

場面 選択 理由
公開 API ゲートウェイ トークンバケット 通常利用が突発的
有料の外部 API 漏れバケット 秒単位の課金で突発に意味がない
データベース接続プール 漏れバケット 突発で接続が尽きる
利用者ごとの上限 トークンバケット 短時間の集中を罰する必要はない

分散環境の落とし穴

プロセス内の計数は、複数インスタンスでは上限を台数分に増やします。共有状態(Redis の INCR と期限)を使うか、各インスタンスに 上限 / 台数 を割り当てます。

Redis では原子性に注意します。GET してから SET は競合します。INCR と EXPIRE、あるいは両方を行う Lua スクリプトを使います。

どの状態コードを返すか

制限時は 429 Too Many Requests を返し、Retry-After で待ち時間を伝えます。Retry-After の無い 429 は盲目的な再試行を招き、自分の流量を増やします。

トークンバケットは長期平均と突発予算を、漏れバケットは一定出力を扱います。下流がどちらを消化できるかを先に決めてください。

← 記事一覧に戻る

コメント

…