ホームページ >ウェブフロントエンド >jsチュートリアル >スロットリングの説明: API リクエスト制限の管理ガイド
大規模なプロジェクトの場合は、通常、Cloudflare Rate Limiting や HAProxy などのツールを使用するのが最善です。これらは強力で信頼性が高く、面倒な作業を代わりに行ってくれます。
ただし、小規模なプロジェクトの場合、または物事がどのように機能するかを知りたい場合は、コード内で独自のレート リミッターを直接作成できます。なぜ?
このガイドを終えるまでに、API が過剰になるのを防ぐために TypeScript で基本的なスロットラーを構築する方法がわかるでしょう。ここで取り上げる内容は次のとおりです:
このガイドは実践的な出発点となるように設計されており、不必要に複雑にすることなく基本を学びたい開発者に最適です。 しかし、本番環境に対応したものではありません。
開始する前に、Lucia のレート制限セクションに適切なクレジットを付与したいと思います。
Throttler クラスを定義しましょう:
export class Throttler { private storage = new Map<string, ThrottlingCounter>(); constructor(private timeoutSeconds: number[]) {} }
Throttler コンストラクターは、タイムアウト期間 (timeoutSeconds) のリストを受け入れます。ユーザーがブロックされるたびに、このリストに基づいて期間が徐々に長くなります。最終的に、最後のタイムアウトに達すると、コールバックをトリガーしてユーザーの IP を永久に禁止することもできます。ただし、それはこのガイドの範囲を超えています。
間隔を増やすためにユーザーをブロックするスロットラー インスタンスを作成する例を次に示します。
const throttler = new Throttler([1, 2, 4, 8, 16]);
このインスタンスは、初めてユーザーを 1 秒間ブロックします。 2回目は2人、というように。
当社はマップを使用して IP アドレスとそれに対応するデータを保存します。マップは頻繁な追加と削除を効率的に処理できるため、理想的です。
プロのヒント: 頻繁に変更される動的データにはマップを使用します。静的で変化しないデータの場合は、オブジェクトの方が適しています。 (ウサギの穴 1)
エンドポイントはリクエストを受信すると、ユーザーの IP アドレスを抽出し、スロットラーに問い合わせてリクエストを許可するかどうかを判断します。
ケース A: 新規または非アクティブなユーザー
IP がスロットラーで見つからない場合、それはユーザーの最初のリクエストであるか、ユーザーが十分な期間非アクティブであったかのいずれかです。この場合:
ケース B: アクティブ ユーザー
IP が見つかった場合は、ユーザーが以前にリクエストを行ったことを意味します。ここ:
後者の場合、最後のブロックから十分な時間が経過したかどうかを確認する必要があります。インデックスのおかげで、どの timeoutSeconds を参照する必要があるかがわかります。そうでない場合は、単にリバウンドしてください。それ以外の場合は、タイムスタンプを更新します。
export class Throttler { private storage = new Map<string, ThrottlingCounter>(); constructor(private timeoutSeconds: number[]) {} }
インデックスを更新するときは、timeoutSeconds の最後のインデックスに制限されます。これがないと、counter.index 1 がオーバーフローし、次に this.timeoutSeconds[counter.index] にアクセスするとランタイム エラーが発生します。
この例は、スロットラーを使用してユーザーが API を呼び出す頻度を制限する方法を示しています。ユーザーが行うリクエストが多すぎると、メイン ロジックが実行されずにエラーが発生します。
const throttler = new Throttler([1, 2, 4, 8, 16]);
ログイン システムでレート制限を使用すると、次の問題が発生する可能性があります:
これを防ぐには、レート制限に IP の代わりにユーザーの一意の userID を使用します。また、不必要なブロックを避けるために、ログインに成功した後にスロットラーの状態をリセットする必要があります。
Throttler クラスにリセット メソッドを追加します:
export class Throttler { // ... public consume(key: string): boolean { const counter = this.storage.get(key) ?? null; const now = Date.now(); // Case A if (counter === null) { // At next request, will be found. // The index 0 of [1, 2, 4, 8, 16] returns 1. // That's the amount of seconds it will have to wait. this.storage.set(key, { index: 0, updatedAt: now }); return true; // allowed } // Case B const timeoutMs = this.timeoutSeconds[counter.index] * 1000; const allowed = now - counter.updatedAt >= timeoutMs; if (!allowed) { return false; // denied } // Allow the call, but increment timeout for following requests. counter.updatedAt = now; counter.index = Math.min(counter.index + 1, this.timeoutSeconds.length - 1); this.storage.set(key, counter); return true; // allowed } }
ログインに成功した後にそれを使用します:
export class Throttler { private storage = new Map<string, ThrottlingCounter>(); constructor(private timeoutSeconds: number[]) {} }
スロットラーは IP とレート制限を追跡するため、不要になった IP レコードをいつどのように削除するかを考えることが重要です。クリーンアップ メカニズムがないと、スロットラーはレコードをメモリに保存し続けるため、データが増大するにつれてパフォーマンスの問題が発生する可能性があります。
これを防ぐには、一定期間非アクティブになった後に古いレコードを定期的に削除するクリーンアップ機能を実装できます。ここでは、スロットラーから古いエントリを削除する簡単なクリーンアップ メソッドを追加する方法の例を示します。
const throttler = new Throttler([1, 2, 4, 8, 16]);
クリーンアップをスケジュールする非常に簡単な方法 (ただし、おそらく最良ではない) は setInterval:
を使用することです。
export class Throttler { // ... public consume(key: string): boolean { const counter = this.storage.get(key) ?? null; const now = Date.now(); // Case A if (counter === null) { // At next request, will be found. // The index 0 of [1, 2, 4, 8, 16] returns 1. // That's the amount of seconds it will have to wait. this.storage.set(key, { index: 0, updatedAt: now }); return true; // allowed } // Case B const timeoutMs = this.timeoutSeconds[counter.index] * 1000; const allowed = now - counter.updatedAt >= timeoutMs; if (!allowed) { return false; // denied } // Allow the call, but increment timeout for following requests. counter.updatedAt = now; counter.index = Math.min(counter.index + 1, this.timeoutSeconds.length - 1); this.storage.set(key, counter); return true; // allowed } }
このクリーンアップ メカニズムは、スロットラーが古いレコードを無期限に保持しないようにし、アプリケーションの効率を維持するのに役立ちます。このアプローチはシンプルで実装が簡単ですが、より複雑なユースケース (より高度なスケジューリングの使用や高い同時実行性の処理など) では、さらに改良する必要がある場合があります。
定期的なクリーンアップにより、メモリの肥大化を防ぎ、しばらくリクエストを試みていないユーザーが追跡されないようにすることができます。これは、レート制限システムをスケーラブルでリソース効率の高いものにするための第一歩です。
冒険心があれば、プロパティがどのように割り当てられ、それがどのように変化するかを読むことに興味があるかもしれません。また、モノモーフィズムで特に好まれるインライン キャッシュなどの VM の最適化についても考えてみましょう。楽しむ。 ↩
以上がスロットリングの説明: API リクエスト制限の管理ガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。