【入門編】 レートリミットの設計:固定ウィンドウアルゴリズム – Web APIアーキテクチャ・データ連携実践ガイド

門番は「お急ぎ便」をどうさばく?固定ウィンドウで学ぶレートリミットの現実

こんにちは。ネットワークとプロトコルの深淵を愛するエンジニアです。

皆さんは、Web APIを設計する際に「誰かが短時間に何万回もリクエストを送ってきたら、サーバーがパンクしちゃうのでは?」と不安になったことはありませんか? 実際、悪意のある攻撃でなくても、プログラムのバグで無限ループが発生し、サーバーをダウンさせる「セルフDOS攻撃」は現場でよくある話です。

これを防ぐための防波堤が「レートリミット(リクエスト制限)」です。今回は、その中でも最もシンプルで、かつ「現場の泥臭い罠」が潜んでいる「固定ウィンドウアルゴリズム」について、郵便配達の例えを交えて紐解いていきましょう。

—

郵便ポストの「1分間ルール」をイメージしよう

固定ウィンドウアルゴリズムは、非常に単純です。「1分間に送れる手紙は最大10通まで」というルールを、時計の針に合わせて機械的に運用する仕組みです。

  • 0分00秒から0分59秒まで: 10通届いたら、あとは門前払い。
  • 1分00秒になった瞬間: カウントをリセットして、また10通まで受付開始。

これが固定ウィンドウの正体です。実装も簡単で、サーバー側で「今この1分間で何回数えたか」をカウンターとして持っておくだけで済みます。

なぜこれが「危うい」のか?

さて、ここで現実世界の「境界線」を想像してみてください。例えば、1分間の区切りが「0秒」だとしましょう。

「0分59秒」に10通の郵便物が殺到し、サーバーがそれを受け取ったとします。そして「1分00秒」になった瞬間にカウンターがリセットされ、また10通が届く。すると、実質的に「0分59秒から1分01秒のわずか2秒間で20通」ものリクエストを処理しなければならなくなります。

これをネットワークの世界では「バーストトラフィック」と呼びます。時計の切り替わりタイミングで負荷がスパイク(急上昇)し、せっかく制限をかけているのにサーバーが悲鳴を上げる……これが固定ウィンドウの最大の弱点なのです。

—

Pythonで見る「固定ウィンドウ」の素朴な実装

では、この仕組みをPythonで擬似的に表現してみましょう。Webアプリケーションのフレームワーク(FlaskやFastAPIなど)でよく使われるロジックを簡略化したものです。

import time

# 制限設定:1分間に最大10リクエスト
LIMIT = 10
WINDOW_SIZE = 60  # 60秒

# メモリ上の簡易カウンター(本番環境ではRedisなどを使います)
request_count = 0
window_start_time = time.time()

def can_process_request():
    global request_count, window_start_time
    
    current_time = time.time()
    
    # ウィンドウ(1分間)が経過したか判定
    if current_time - window_start_time > WINDOW_SIZE:
        request_count = 0
        window_start_time = current_time
        
    if request_count < LIMIT:
        request_count += 1
        return True
    else:
        # 制限超過時は False を返す(クライアントには 429 Too Many Requests を返す)
        return False

このコードのポイントは、time.time() で現在時刻を監視し、一定時間が経過したらカウンターをゼロに戻している点です。非常にシンプルですが、先ほどお話しした「境界線でのバースト」には無防備です。

—

現場で役立つ「レートリミット」設計の知恵

現場のインフラエンジニアとして、この「固定ウィンドウ」を運用する際に気をつけているポイントを3つだけお伝えします。

1. HTTPステータスコード 429 を正しく返す

制限に達した際、単に接続を切るのではなく、429 Too Many Requests というステータスを返すのがマナーです。これを受け取ったクライアント側は、「ああ、今は混んでいるから少し待とう」と判断でき、無駄なリクエストを抑えることができます。

2. 「猶予」を考えるならヘッダーを活用する

APIのレスポンスヘッダーに、現在の状況を乗せてあげると親切です。

  • X-RateLimit-Limit: 最大許可数
  • X-RateLimit-Remaining: 残り回数
  • Retry-After: あと何秒待てばリクエストできるか

これらを返すと、クライアント側の開発者が「お行儀の良いアプリ」を作るための助けになります。

3. 固定ウィンドウの限界を知る

もし、境界線のスパイクがどうしても許容できないサービス(決済APIや、リアルタイム性が極めて高いシステム)であれば、固定ウィンドウではなく「スライディングウィンドウ」や「トークンバケット」といった、もっと滑らかな制限アルゴリズムを検討しましょう。これらは、時計の針に依存せず、より緩やかにトラフィックを平準化してくれます。

—

最後に:ネットワークは「思いやり」でできている

レートリミットは、サーバーを守るための盾ですが、同時に「クライアントとサーバーの健全な関係」を維持するためのルールブックでもあります。

最初は「固定ウィンドウ」で始めてみて、トラフィックの波を見ながら、必要に応じてより高度な仕組みへステップアップする。この「泥臭い観察と調整」こそが、インフラエンジニアの醍醐味です。

まずはシンプルなカウンターから始めて、パケットが制限に跳ね返される様子をログで眺めてみてください。きっと、ネットワークの鼓動が少しだけ身近に感じられるはずですよ。

コメント

タイトルとURLをコピーしました