質問

私のサイトの正当なユーザーは時折、望ましくない結果を引き起こすAPIリクエストでサーバーをハンマー。私はこれ以上言うよりも1 API呼び出しの5秒ごとまたは毎分n個のコールを制限を提起したい(まだ正確な制限を考え出したていません)。私は明らかにDB内のすべてのAPI呼び出しを記録し、彼らが制限を超えているかどうかを確認するために、すべてのリクエストに応じて計算を行いますが、すべての要求のすべてのこの余分なオーバーヘッドは目的を破っことになる可能性があります。私は制限を制定するために使用することができ、他の少ないリソースを集中的な方法は何ですか?私は何もの価値のために、PHP / Apacheの/ Linuxを使用しています。

役に立ちましたか?

解決

[OK]を、私はのサーバに任意のの書き込みが、私は、少なくとも一つ一つのリクエストをログに記録排除することができずに尋ねた何をする方法はありません。一つの方法は、それが最後のリクエスト($last_api_request)及びリクエスト/時間枠($minute_throttle)の制限の数の比を追跡する方法を絞る「リーキーバケット」を使用することです。リーキーバケットは、(時間ごとにリセットされますTwitterのAPIのスロットルとは違って)そのカウンタをリセットすることはありませんが、バケツがいっぱいになった場合(ユーザーが限界に達した)、彼らは別の要求を行うことができます前に、少しを空にするバケット用n秒を待たなければなりません。言い換えれば、それは、圧延限界のようだ:時間枠内で、前の要求がある場合、彼らはゆっくりとバケツから漏れるされています。あなたはバケツを埋める場合、それはあなただけを制限します。

このコードスニペットは、リクエストごとに新しい$minute_throttle値を計算します。あなたは、毎日、など時間ごとなど、任意の期間のためなどにスロットルを追加することができますので、1はすぐにそれがユーザーのために混乱作るために開始します以上が、私は... $minute_throttleの分を指定しました。

$minute = 60;
$minute_limit = 100; # users are limited to 100 requests/minute
$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds
$last_api_diff = time() - $last_api_request; # in seconds
$minute_throttle = $this->get_throttle_minute(); # get from the DB
if ( is_null( $minute_limit ) ) {
    $new_minute_throttle = 0;
} else {
    $new_minute_throttle = $minute_throttle - $last_api_diff;
    $new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;
    $new_minute_throttle += $minute / $minute_limit;
    $minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute  );
    # can output this value with the request if desired:
    $minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;
}

if ( $new_minute_throttle > $minute ) {
    $wait = ceil( $new_minute_throttle - $minute );
    usleep( 250000 );
    throw new My_Exception ( 'The one-minute API limit of ' . $minute_limit 
        . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );
}
# Save the values back to the database.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );

他のヒント

あなたはリーキーバケットに匹敵する、トークンバケットアルゴリズムにして速度を制御することができますアルゴリズム。あなたがプロセスを超えるバケット(トークンのすなわち量)(または任意の範囲をコントロールしたい)の状態を共有する必要があることに注意してください。あなたは競合状態を避けるためにロックを考えるしたい場合がありますので。

良いニュース:私はあなたのためにすべてのことをした:帯域幅スロットル/トークンバケット

use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$storage = new FileStorage(__DIR__ . "/api.bucket");
$rate    = new Rate(10, Rate::SECOND);
$bucket  = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);

if (!$bucket->consume(1, $seconds)) {
    http_response_code(429);
    header(sprintf("Retry-After: %d", floor($seconds)));
    exit();
}

このスレッドがまだ生きているかどうかはわからないが、私はmemcachedのようにメモリキャッシュにこれらの統計を維持することをお勧め。これは、DBへのリクエストをログ記録のオーバーヘッドを減らすが、それでも目的を果たすだろう。

最も簡単な解決策は、単に各APIキーを24時間あたりの要求の数が限られて与え、そしていくつかの既知の固定、一度にそれらをリセットすることになります。

彼らは彼らのAPIリクエスト使い果たした場合(すなわち。カウンターに達するが、方向あなたしているカウントに応じて、ゼロ、または制限)、ストップあなたが彼らのカウンタをリセットするまで、それらのデータを提供します。

この方法では、それはリクエストであなたをハンマーしないように、の自分のの最善の利益になります。

あなたは「EVERY要求上のすべてのTHOS余分なオーバーヘッドが目的を破っされるだろう」と言うが、私は正しいだとよく分かりません。サーバーのハンマリングを防止する目的ではないですか?これはおそらく、それは本当に唯一の迅速な読み取り/書き込みを必要とするため、私は、それを実装するような方法です。あなたは、パフォーマンスを心配している場合は、異なるDB /ディスクへのAPIサーバーのチェックを耕作することができます。

しかし、あなたは選択肢をしたい場合は、あなたがチェックアウトする必要があります mod_cband に、サードパーティのApacheモジュール帯域幅調整を支援するために設計されています。主に帯域幅制限のためにもかかわらず、それは同様の要求毎秒に基づいてスロットルすることができます。私はそれを使ったことがないので、私はあなたが取得したい結果の種類はよく分かりません。そこMOD-スロットルと呼ばれる別のモジュールも同様だったが、そのプロジェクトは現在閉じているように見える、とApache 1.3シリーズ以上何のためにリリースされていませんでした。

最初から実装に加えて、あなたも3scaleのようなAPIのインフラを見てみることができます(のhttp://www.3scale .NET の)速度制限、ならびに他のものの束をした(分析など)。それのためのPHPプラグインがあります: https://github.com/3scale/3scale_ws_api_for_phpする

また、APIのワニスインフロントのようなものを固執し、そのように制限API率を行うことができます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top