ホームページ >ウェブフロントエンド >jsチュートリアル >スロットリングの説明: API リクエスト制限の管理ガイド

スロットリングの説明: API リクエスト制限の管理ガイド

DDD
DDDオリジナル
2024-12-05 22:29:10476ブラウズ

コードにスロットリングを実装する必要があるのはどのような場合ですか?

大規模なプロジェクトの場合は、通常、Cloudflare Rate Limiting や HAProxy などのツールを使用するのが最善です。これらは強力で信頼性が高く、面倒な作業を代わりに行ってくれます。

ただし、小規模なプロジェクトの場合、または物事がどのように機能するかを知りたい場合は、コード内で独自のレート リミッターを直接作成できます。なぜ?

  • シンプルです: 理解しやすい単純なものを構築します。
  • 予算に優しい: サーバーのホスティング以外に追加費用はかかりません。
  • 小規模プロジェクトに適しています: トラフィックが少ない限り、作業を高速かつ効率的に維持できます。
  • 再利用可能: 新しいツールやサービスを設定せずに、他のプロジェクトにコピーできます。

何を学ぶか

このガイドを終えるまでに、API が過剰になるのを防ぐために TypeScript で基本的なスロットラーを構築する方法がわかるでしょう。ここで取り上げる内容は次のとおりです:

  • 設定可能な時間制限: 試行がブロックされるたびに、悪用を防ぐためにロックアウト期間が長くなります。
  • リクエストの上限: 許可されるリクエストの最大数を設定します。これは、OpenAI などの有料サービスを伴う API に特に役立ちます。
  • メモリ内ストレージ: Redis などの外部ツールなしで動作するシンプルなソリューション。小規模なプロジェクトやプロトタイプに最適です。
  • ユーザーごとの制限: IPv4 アドレスを使用してユーザーごとにリクエストを追跡します。 SvelteKit を利用して、組み込みメソッドでクライアント IP を簡単に取得します。

このガイドは実践的な出発点となるように設計されており、不必要に複雑にすることなく基本を学びたい開発者に最適です。 しかし、本番環境に対応したものではありません

開始する前に、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 がスロットラーで見つからない場合、それはユーザーの最初のリクエストであるか、ユーザーが十分な期間非アクティブであったかのいずれかです。この場合:

    • アクションを許可します。
    • 初期タイムアウトを使用して IP を保存することでユーザーを追跡します。
  • ケース B: アクティブ ユーザー

    IP が見つかった場合は、ユーザーが以前にリクエストを行ったことを意味します。ここ:

    • 最後のブロックから必要な待ち時間 (timeoutSeconds 配列に基づく) が経過したかどうかを確認します。
    • 十分な時間が経過した場合:
    • タイムスタンプを更新します。
    • タイムアウト インデックスをインクリメントします (オーバーフローを防ぐために最後のインデックスに制限されます)。
    • そうでない場合は、リクエストを拒否します。

後者の場合、最後のブロックから十分な時間が経過したかどうかを確認する必要があります。インデックスのおかげで、どの 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]);

Throttling Explained: A Guide to Managing API Request Limits

認証に関する注意事項

ログイン システムでレート制限を使用すると、次の問題が発生する可能性があります:

  1. ユーザーがログインすると、スロットラーがタイムアウトを IP に関連付けます。
  2. ユーザーがログアウトするか、セッションが終了します (例: すぐにログアウトする、セッションとともに Cookie が期限切れになり、ブラウザがクラッシュするなど)。
  3. その後すぐに再度ログインしようとすると、スロットラーが依然としてブロックし、429 Too Many Requests エラーを返す可能性があります。

これを防ぐには、レート制限に 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 とレート制限を追跡するため、不要になった 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
    }
}

このクリーンアップ メカニズムは、スロットラーが古いレコードを無期限に保持しないようにし、アプリケーションの効率を維持するのに役立ちます。このアプローチはシンプルで実装が簡単ですが、より複雑なユースケース (より高度なスケジューリングの使用や高い同時実行性の処理など) では、さらに改良する必要がある場合があります。

定期的なクリーンアップにより、メモリの肥大化を防ぎ、しばらくリクエストを試みていないユーザーが追跡されないようにすることができます。これは、レート制限システムをスケーラブルでリソース効率の高いものにするための第一歩です。


  1. 冒険心があれば、プロパティがどのように割り当てられ、それがどのように変化するかを読むことに興味があるかもしれません。また、モノモーフィズムで特に好まれるインライン キャッシュなどの VM の最適化についても考えてみましょう。楽しむ。 ↩

以上がスロットリングの説明: API リクエスト制限の管理ガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。