Carpe Diem

備忘録

様々なrate limitアルゴリズム

概要

インターネットに晒されているWebサービスでは

  • TV等で紹介されたことによる大量流入
  • 悪意ある人物からの攻撃
  • クライアントのバグに依る大量リクエス

など、本来想定していた以上のトラフィックが来ることはよくあります。
単純にシステムを構築すると大規模トラフィックに対応できずシステムがスローダウンしてしまうため、何かしらrate limitをかけておいた方が良いです。

ただしrate limitと一口に入っても色々あるため、今回は主なrate limitアルゴリズムを紹介します。

Leaky bucket

Leaky bucketはデータ転送レートを一定にする(=上限を設定する)アルゴリズムです。
下の図のように、様々な流量の水流がそのバケツに流れ込んでも小さな穴からは一定の水流が流れ出す仕組みです。

f:id:quoll00:20191024063454j:plain

ref: What is the difference between token bucket and leaky bucket algorithms? - Quora

メリット

デメリット

  • バースト性のあるトラフィックに向いてない
    • 図だとバーストしたリクエストをバケツで保持しているように見えるが、実際は容量を超えたトラフィックは破棄されるロジックが多い

Token bucket

Leaky bucketが上限を設定するのに対し、Token bucket平均に制限を課してある程度のバースト性を許容します。
仕組みは以下のフローで

f:id:quoll00:20191024064641p:plain

ref: 限流算法之令牌桶与漏桶算法 | 0xCAFEBABE

  1. 定期的にtokenが発行される
  2. tokenが残っていればトラフィックを通す
  3. tokenが残っていないればトラフィックをキューで保持(破棄するロジックも多い)
  4. トラフィックがなければ一定量tokenを保持できる

という形です。最後のtoken保持の仕組みにより、一時的にバーストリクエストが来てもtokenが残っている限り通すことができます。

AWSのCPUクレジットやEBSバーストバランスがまさしくこのアルゴリズムですね。

メリット

Fixed window counters

Fixed window counters[00:00, 00:01), [00:01, 00:02), ...[23:59, 00:00)のように固定した期間でのリクエスト量を制限するアルゴリズムです。

例えば以下の図では各期間2リクエストまでとなっており、3リクエスト目以降は破棄されます。
しかし新しい期間になったら再び上限までリクエストを受け付けます

f:id:quoll00:20191024071926p:plain

ref: Rate Limiting Part 1

メリット

  • シンプルで分かりやすいし実装もしやすい

デメリット

注意点としてはFixed windowの名の通り期間が固定されているため、リクエストが期間境界に偏ると上限の倍のリクエストが来ることになります。

例)[00:00:30, 00:01:00)に2リクエスト、[00:01:00, 00:01:30)に2リクエスト来た場合、[00:00:30, 00:01:30)1分間に上限の倍の4リクエスト来たことになる

Sliding window log

先程のFixed window countersの問題点である期間境界の偏りに対応するアルゴリズムです。

2 req/minと制限した例で説明すると、

f:id:quoll00:20191024074302p:plain

ref: Rate Limiting Part 1

  1. 00:00:12にリクエストがくる。00:00:12を含め過去1分間のリクエストは1つ2 req/minを満たすので許可
  2. 00:00:24にリクエストがくる。00:00:24を含め過去1分間のリクエストは00:00:12と合わせて2つ2 req/minを満たすので許可
  3. 00:00:36にリクエストがくる。00:00:36を含め過去1分間のリクエストは3つ2 req/minを満たさないので拒否
  4. 00:01:25にリクエストがくる。00:01:25を含め過去1分間のリクエストは00:00:36と合わせて2つ2 req/minを満たすので許可

という仕組みです。

メリット

  • 正確に2 req/minといった制限ができるところ

デメリット

  • その期間のログを常に保持してハンドリングする必要があるので実装コストやメモリコストが高い

Sliding window counters

Fixed window countersSliding window logを組み合わせ、それぞれの問題点を解決させたアルゴリズムです。

10 req/minという上限を設定した場合に、

f:id:quoll00:20191024080551p:plain

ref: Rate Limiting Part 1

  1. [00:00, 00:01)の期間に9リクエスト来た
  2. 00:01:15の段階で4リクエスト目が来た
  3. 00:01:15[00:01, 00:02)期間の25%。前期間のweightは75%9 x 0.75 + 4 = 10.75 > 10で上限を超えたので拒否
  4. 00:01:30時点では前期間weightが50%9 x 0.5 = 4.5なので、5リクエスト目までOK

というように、現期間のリクエストタイミングにより前期間にweightを付けて上限以内かどうかチェックするアルゴリズムです。

メリット

これはweightによる計算のため小数点以降の値が出てくるため正確な制限にはなりませんが、

  • Fixed window countersの境界問題が解決されている
  • Sliding window logのようにログを保持するコストがない

という点で改良されてます。

まとめ

rate limitの様々なアルゴリズムを紹介しました。
実務ではAWSのWAFなどマネージドを採用するケースが多いと思いますが、このようにアルゴリズムを知っていればユースケースに応じて選択・自作することもできると思います。

ソース