- レート制限は、クライアントが一定期間に実行できるリクエストの数を制限します。
- トークン バケットは、平均レートを強制しながら短いバーストを許可します。
- 単純な 固定ウィンドウ では、ウィンドウ境界で制限の 2 倍を通過できます。
- Retry-After ヘッダーを付けて 429 Too Many Requests を返し、クライアントにジッターを発生させてバックオフさせます。
1 つのバグのあるスクリプトが緊密なループで API を呼び出します。制限がないと、他のすべてのユーザーのサービスが遅くなったり、多額のクラウド料金が発生したりする可能性があります。 API レート制限 は、単一のクライアントがその公平なシェアを超えて使用することを防ぐガード レールです。
一般的なアルゴリズムがどのように動作するかを示すために、小さな Python バージョンを作成し、シミュレートされたリクエストのタイミングを与えました。以下の出力はその実行からのものです。これらは実稼働コードではなく教育モデルであり、実際のゲートウェイにはさらに多くの機能があります。
レート制限とは何ですか?
レート制限では、クライアントごとのリクエストをカウントし、合意された制限 (1 分あたり 100 リクエストなど) を超えるリクエストを拒否または遅延します。関連するアイデアである スロットリング は、多くの場合、リクエストを拒否するのではなく、リクエストを遅くすることを意味します。 クォータは通常、料金プランでの月あたり 10,000 回の通話など、長期間にわたるより大きな許容量です。
API にそれが必要な理由
- 安定性: トラフィックの急増によってデータベースやダウンストリーム サービスが圧倒されることはありません。
- 公平性: 1 つのヘビー クライアントが他のクライアントを飢えさせることはできません。
- セキュリティ: 制限により、総当たりログイン試行とスクレイピングが遅くなります。
- コスト管理: AI モデルの呼び出しなどの高価な操作は予算内に収まります。
レート制限は、無制限のリソース消費の下での OWASP API セキュリティ トップ 10 の中核となる制御です。 Postman を使用した API テストガイドでは、チェックに必要な繰り返しリクエストを送信する方法を示しています。
トークンバケット
最大 5 つのトークンを保持するバケツを想像してください。各リクエストにはトークンが必要です。トークンは 1 秒あたり 1 個ずつ補充され、バケットには 5 個を超えるトークンが保持されることはありません。バケットが空の場合、リクエストは拒否されます。
class TokenBucket:
def __init__(self, capacity, refill_per_sec):
self.capacity = capacity
self.rate = refill_per_sec
self.tokens = capacity
self.last = 0.0
def allow(self, now):
elapsed = now - self.last
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last = now
if self.tokens >= 1:
self.tokens -= 1
return True
return False
一度に 7 つのリクエストを送信し、後でさらにリクエストを送信しました。
burst of 7 at t=0: [True, True, True, True, True, False, False]
t=2.0: [True, True, False]
t=10: 5 allowed of 10
最初の 5 個はバーストとして実行され、その後バケツは空になりました。 2 秒後、2 つのトークンが補充されたため、さらに 2 つのリクエストが通過しました。長い一時停止の後、バケットは再びいっぱいになりましたが、上限は 5 でした。このバースト後に定常的な動作は実際のトラフィックに適しており、これが API ゲートウェイでトークン バケットが一般的である理由です。 リーキー バケットは、リクエストを固定レートで処理し、バーストを許可する代わりに出力を平滑化する近い親戚です。
固定窓と引き違い窓
固定ウィンドウカウンターは最も単純なアプローチです。1 分あたり 10 件のリクエストを許可し、分が変わるとカウンターをリセットします。境界に既知の欠陥があります。 59.9 秒で 10 件のリクエストを送信し、60.0 秒でさらに 10 件のリクエストを送信しました。60 秒のウィンドウごとに 10 件の制限があります。
fixed window allowed around boundary: 20 of 20
sliding log allowed around boundary: 10 of 20
固定ウィンドウでは、20 のリクエストすべてが 2 つの異なるウィンドウに分割されたため、10 分の 1 秒以内に許可され、意図した速度の 2 倍になりました。 スライディング ログは、最近の各リクエストの時間を記憶し、過去 60 秒間のリクエストをカウントするため、正しく許可されるのは 10 件のみでした。
トレードオフはメモリです。スライディング ログにはリクエストごとのタイムスタンプが保存されます。 スライディング ウィンドウ カウンターは、人気のある中間点です。現在のウィンドウ数と以前のウィンドウ数をブレンドすることでレートを推定し、少ないメモリを使用して高い精度でレートを推定します。
アルゴリズムの比較
| アルゴリズム | バースト | 精度 | 記憶 |
|---|---|---|---|
| 固定窓 | 境界で最大 2 倍 | 低い | 非常に低い |
| スライド丸太 | いいえ | 正確な | 高 |
| 引き違い窓のカウンター | 小 | 良い | 低い |
| トークンバケット | 容量まで許可される | 良い | 低い |
| 漏れのあるバケツ | 滑らかになった | 良い | 低い |
何を制限するか
- API キーまたはユーザー ID: 認証された API の最も公平なオプション。
- IP アドレス: はパブリック エンドポイントに便利ですが、多くのユーザーが 1 つの IP を共有することができ、攻撃者はそれらをローテーションする可能性があります。
- エンドポイント: 検索、エクスポート、ログインなどの高コストの操作には、より厳しい制限が必要です。
- プラン階層: 無料の顧客と有料の顧客では、異なる制限が適用されることがよくあります。
ログインエンドポイントには特別な注意が必要です。アカウントと IP の両方で制限するため、攻撃者は単純に推測を多数の IP または多数のアカウントに広めることはできません。
429 応答
クライアントが制限を超えると、500 や 403 ではなく、HTTP 429 Too Many Requests を返します。何が起こったのか、いつ再試行するかをクライアントに伝えるヘッダーを含めます。私たちのシミュレーションでは、リクエストが残っていないクライアントに対して次のセットが生成されました。
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
Retry-After: 30
Retry-After は標準の HTTP ヘッダーです。 RateLimit-* ヘッダーは IETF ドラフトから来ており、多くの API は独自の X-RateLimit-* バリアントを使用しています。何を選択したとしても、それを文書化してください。ステータス コードの詳細については、HTTP ステータス コードのチートシートを参照してください。
クライアントとして制限を処理する
429 の直後に再試行すると、状況が悪化します。数千のクライアントが同時にこれを実行すると、同期した波でサーバーにアクセスします。修正は、ジッターを伴う指数バックオフです。失敗するたびに長く待機し、ランダム性を追加します。
def backoff(attempt, base=0.5, cap=30, rng=random.Random(4)):
return round(min(cap, base * 2 ** attempt) * rng.random(), 2)
[backoff(a) for a in range(6)]
# [0.12, 0.1, 0.79, 0.62, 0.53, 6.43]
これは「フル ジッター」です。ゼロと指数関数的に増加する上限の間のランダムな待機です。待ち時間は不規則に見えますが、それがポイントです。クライアントは一緒に再試行するのではなく、分散します。出力を再現できるように、ランダム ジェネレーターをシードしました。
クライアント チェックリスト: Retry-After が存在する場合はそれを尊重し、再試行回数に上限を設け、繰り返しても安全なリクエストのみを再試行し、制限に達する前に速度を落とすことができるように Remaining-Requests ヘッダーを監視します。
分散型レート制限
ロード バランサーの背後に複数の API サーバーがあるため、各サーバーが独自にカウントすることで、クライアントがリクエストを分散して制限を超えることができます。チームは通常、アトミック操作または短いスクリプトを使用して Redis などの共有ストアにカウンターを保持するため、2 つのサーバーが両方とも最後のトークンを取得することはできません。 Kong、NGINX、Envoy、クラウド API ゲートウェイなどのゲートウェイは、これを構成として提供します。この記事では分散セットアップを実行しませんでした。
レート制限のテスト
- 制限までリクエストを送信し、成功することを確認します。
- もう 1 つ送信し、正しいヘッダーを持つ 429 を確認します。
- リセット時間まで待って、リクエストが成功したことを再度確認します。
- キーごとの制限を確認します。あるクライアントの使用が別のクライアントをブロックしてはなりません。
- ダブルバースト問題についてウィンドウ間の境界をテストします。
- 負荷テストとストレス テストで説明されているように、負荷ツールを使用して、同時実行性が高い場合の動作をチェックします。
単体テストでは、シミュレーションが now で行うように、現在時刻を挿入します。これにより、テストがスリープする必要がなくなります。
ユーザーが許容できる制限を設計する
数値を選択することは、アルゴリズムを選択するのと同じくらい重要です。実際のトラフィックから始めます。最も負荷の高い正規クライアントが 1 分あたりに行うリクエストの数を確認し、それよりも無理なく制限を設定します。通常の使用をブロックする制限により、安全性ではなくサポート チケットが作成されます。
- API ドキュメントでエンドポイントごと、プランごとに 制限を公開します。
- ブロックする前に警告します。 残りのリクエストを示すヘッダーにより、行儀の良いクライアントが勝手に速度を落とす可能性があります。
- Webhook、バルク エンドポイント、大きなページなど、大量のポーリングの代替手段を提供します。
- 429 レートを監視します。 突然の上昇は、攻撃、バグのあるクライアントのリリース、または現在厳しすぎる制限を意味する可能性があります。
- 例外を意図的に許可する。たとえば、パートナーの制限を引き上げ、ハードコーディングするのではなく構成に記録します。
また、制限をわずかに超えて到着したリクエストをどうするかについても決定します。一部の API は、少数の追加リクエストを拒否するのではなく、一時的にキューに入れて、短いスパイクを平滑化します。また、分析などのそれほど重要ではないエンドポイントよりも、支払いなどの重要なエンドポイントを優先して、システムに負荷がかかっているときに重要なトラフィックが流れ続けるようにするものもあります。
まとめ
適切なレート制限は、API とクライアントの間の契約です。サーバーは公正なアルゴリズム (通常はトークン バケットまたはスライディング ウィンドウ) を選択し、その制限を 429 と Retry-After で明確に説明します。クライアントはこれらの信号を尊重し、ジッターを発生させて後退します。 API キーごとに十分な制限を設定して開始し、実際の使用状況を監視して、必要な場合にのみ制限を強化します。
よくある質問
API レート制限とは何ですか?
これは、クライアントが一定時間内に API に対して実行できるリクエストの数の制限です (1 分あたり 100 件など)。制限を超えるリクエストは拒否されるか、遅延されます。
HTTP 429 とはどういう意味ですか?
429 リクエストが多すぎる場合は、クライアントがレート制限を超えたことを意味します。応答には、待機時間を示す Retry-After ヘッダーが含まれることがよくあります。
トークンバケットアルゴリズムとは何ですか?
各リクエストは、最大まで一定の速度で補充されるバケットからのトークンを使用します。平均レートを強制しながら、短いバーストを許可します。
レート制限とスロットルの違いは何ですか?
レート制限では、通常、制限を超えるリクエストが拒否されます。多くの場合、スロットルによって速度が低下したり、キューに入れられたりします。これらの用語は頻繁に同じ意味で使用されます。
クライアントはレート制限をどのように処理する必要がありますか?
Retry-After を尊重し、指数バックオフとジッターを使用して再試行し、再試行回数を制限し、残りのリクエストが少なくなった場合は速度を落とします。
学び続ける
テスト、WordPress、または SEO についてサポートが必要ですか?
私たちは、チームがより優れたソフトウェアを出荷し、サイトを成長させるのを支援します。あなたが取り組んでいることを教えてください。明確な次のステップを提案します。
詳しいガイドはこちら テスト、AI、最新の開発
この記事は気に入りましたか?トピックを選択して、最新のチュートリアルに直接アクセスしてください。
AI 幻覚の説明: 言語モデルが物事をでっち上げる理由とそれを軽減する方法
AI 幻覚について説明しました。幻覚とは何か、言語モデルが幻覚を生成する理由、一般的な種類、グラウンディングとプロンプトで幻覚を軽減する方法、そして私たちが実行した簡単なチェックです。
記事を読む →DevOps環境変数の説明: .env ファイル、シークレット、および設定が正しく行われている
実行した Python の例を使用して環境変数について説明します。環境変数の読み取りと設定、すべての値が文字列である理由、.env ファイル、プロセスの継承、シークレット、検証について説明します。
記事を読む →ソフトウェア開発API レート制限の説明: アルゴリズム、ヘッダー、および 429 エラーの処理
API レート制限については、実行したシミュレーションで説明します。トークン バケット、固定ウィンドウ、スライディング ウィンドウ、429 応答、レート制限ヘッダー、バックオフによる再試行、およびテスト制限です。
記事を読む →テスト自動化Python でのモッキング:unittest.mock、patch、およびテストを嘘にする間違い
Python でのunittest.mockによるモック、テスト済み: モックとreturn_value、side_effect、名前が使用されている場所のパッチ、仕様と自動仕様、日付のモック、およびよくある間違い。
記事を読む →ソフトウェアテストコードカバレッジの説明: 回線カバレッジとブランチカバレッジ、および 100% の実際の意味
実際のcoverage.pyの実行でコードカバレッジを説明: 行、分岐、条件のカバレッジ、レポートの読み取り、100%のカバレッジでもバグを見逃す可能性がある理由、および賢明なターゲット。
記事を読む →データベースとSQLSQL における DELETE、TRUNCATE、DROP: 違い、ロールバック、およびそれぞれをいつ使用するか
DELETE、TRUNCATE、DROP について、テスト済み SQL を使用して説明します。それぞれの削除内容、ロールバック、トリガー、ID リセット、速度、PostgreSQL、MySQL、SQL Server、SQLite の違いについて説明します。
記事を読む →データベースとSQLSQL ビューの説明: 作成、使用、落とし穴の回避、例を示します
SQL ビューについて、実行したクエリとともに説明しました。ビューの作成、ライブ結果、列の非表示、パフォーマンス、更新可能なビューと具体化されたビュー、壊れた依存関係、ベスト プラクティスについて説明します。
記事を読む →ソフトウェア開発TypeScript インターフェイスと型: 実際の違い、コンパイラでテスト
TypeScript インターフェイスと型、実際のコンパイラでチェック: 両方でできること、片方だけでできること、宣言のマージ、拡張と交差、および従うべき簡単なルール。
記事を読む →ソフトウェア開発Python *args と **kwargs の説明: 柔軟な関数と明確な例
Python の *args と **kwargs について、実行したコードで説明しました。パッキングとアンパッキング、パラメーターの順序、キーワードのみの引数と位置のみの引数、デコレーター、一般的なエラーです。
記事を読む →ソフトウェア開発Python Lambda 関数: 構文、実際の用途、および使用すべきでない場合
Python のラムダ関数について、実行したコードとともに説明します。構文、キーによるソート、マップとフィルター、クロージャー、遅延バインディング トラップ、通常の def がより適切な選択である場合。
記事を読む →人工知能AIエージェントとは何ですか?シンプルなエージェント ループを実行できる仕組み
AIエージェントとは何ですか?エージェント ループ、ツール、メモリ、ガードレール、実行した小さな Python ループ、実際のリスク、およびシンプルなチャットボットがより良い選択である場合についてのわかりやすいガイドです。
記事を読む →DevOpsモニタリングと可観測性: ログ、メトリクス、トレースによる本当の違い
モニタリングと可観測性について説明: 既知および未知の障害、ログ、メトリクスとトレース、チェックアウト インシデントのウォークスルー、ゴールデン シグナル、OpenTelemetry、および便利なアラート。
記事を読む →DevOpsDocker ボリュームとバインド マウント: コンテナ データを正しい方法で保持する
Docker ボリュームとバインド マウントについて説明します。コンテナ データが消える理由、名前付きボリューム、バインド マウント、tmpfs、Compose の例、バックアップ、アクセス許可、およびそれぞれをいつ使用するかについて説明します。
記事を読む →テスト自動化TDD と BDD: 主な違い、1 つの機能が両方の方法で構築され、それぞれをいつ使用するか
TDD と BDD は、両方の方法で構築された 1 つの割引機能で説明されています。実行したコードによるレッド-グリーン-リファクタリング、Given-When-Then シナリオ、ツール、トレードオフ、およびそれぞれをいつ使用するかです。
記事を読む →ソフトウェア開発Git mergeとrebaseの違い:例と使い分け
Git mergeとrebaseの違いを、実際のログ出力、比較表、rebaseの黄金律、安全なチームワークフローで解説します。
記事を読む →ソフトウェア開発Pythonのリスト内包表記:構文・例・使うべきでない場面
Pythonのリスト内包表記を解説:構文、フィルタ、if-else、ネストしたループ、実測した処理時間、そして通常のforループの方が良い場面。
記事を読む →データベースとSQLSQL JOINを例で解説(Inner・Left・Right・Full)
サンプルのテーブルとクエリでSQL JOINを順に解説。INNER、LEFT、RIGHT、FULL、CROSS、SELFに加え、よくある間違いとパフォーマンスのコツも紹介します。
記事を読む →