写真:ワンダーレーン、 Flickr
NGINXは素晴らしいです! しかし、リクエストの処理速度を制限することに関する彼のドキュメントは、それが多少制限されているように思えました。 そこで、NGINXでのレート制限とトラフィックシェーピングに関するこのガイドを書くことにしました。
私たちはするつもりです:
- NGINXディレクティブについて説明します。
- NGINXの受け入れ/拒否ロジックを処理し、
- さまざまな設定でトラフィックのバーストの処理を視覚化します。
さらに、この記事のテストを実験および再現するためのGitHubリポジトリとDockerイメージを作成しました。 実際に学ぶことは常に簡単です。
NGINX要求速度制限ディレクティブ
この記事では、ディレクティブlimit_req_zone
、 limit_req
、 limit_req_status
およびlimit_req_level
を実装するlimit_req
についてlimit_req_status
しlimit_req_level
。 これらにより、拒否されたリクエストのHTTPリクエストステータスコードのステータスを制御したり、これらの失敗をログに記録したりできます。
ほとんどの場合、リクエストの拒否のロジックで混乱します。
最初に、 zone
パラメーターを必要とするlimit_req
ディレクティブを処理する必要がzone
ます。 オプションの burst
およびnodelay
もありnodelay
。
ここでは、次の概念が使用されます。
zone
は、「バケット」、つまり着信リクエストが読み取られる共有スペースを定義します。 1つの「バケット」に分類されるすべてのリクエストは、そのコンテキストでカウントおよび処理されます。 これにより、URL、IPアドレスなどに基づいて制限を設定できます。
burst
はオプションのパラメーターです。 確立されると、確立された基本レート制限を超えて処理できるリクエストの数を決定します。burst
は速度ではなく、リクエスト数の絶対値であることを理解することが重要です。
-
nodelay
は、burst
と共有されるオプションのパラメータでもあります。 以下に、なぜそれが必要なのかを理解します。
NGINXは、リクエストを受け入れるか拒否するかをどのように決定しますか?
ゾーンを設定すると、その速度が設定されます。 たとえば、 300r/m
では、1分あたり300件のリクエストが受け入れられ、 5r/s
では、1秒あたり5件のリクエストが受け付けられます。
ディレクティブの例:
-
limit_req_zone $request_uri zone=zone1:10m rate=300r/m;
-
limit_req_zone $request_uri zone=zone2:10m rate=5/s;
これら2つのゾーンには同じ制限があることを理解することが重要です。 NGINXは、 rate
パラメーターを使用して、頻度を計算し、それに応じて、新しい要求を受信できる間隔を計算します。 この場合、NGINXは漏出バケットと呼ばれるアルゴリズムを使用します。
NGINXの場合、 300r/m
と5r/s
同じです。0.2秒ごとに1つのリクエストをスキップします。 この場合、NGINXはリクエストを受信できるように0.2秒ごとにフラグを設定します。 このゾーンに適したリクエストが到着すると、NGINXはフラグをクリアし、リクエストを処理します。 次の要求が到着し、パケット間の時間をカウントするタイマーがまだ機能していない場合、要求はステータスコード503で拒否されます。時間が切れ、フラグが受信を許可する値に既に設定されている場合、アクションは実行されません。
要求の処理とトラフィックのシェーピングの速度を制限する必要がありますか?
burst
パラメータについて説明しましょう。 上記で説明したフラグが1より大きい値を取ることができると想像してください。 この場合、NGINXが単一のバースト内でスキップする必要があるリクエストの最大数を反映します。
これは、もはや「漏れやすいバケツ」ではなく、「マーカーバスケット」(トークンバケット)です。 rate
パラメーターはリクエスト間の時間間隔を決定しますが、true / falseトークンではなく、 0
1 + burst
カウンターを処理し0
。 カウンターは、計算された時間間隔が経過するたびに増分され(タイマーがトリガーされ)、 b+1
最大値に達します。 繰り返しますが、 burst
はリクエストの数であり、送信の速度ではありません。
新しいリクエストが到着すると、NGINXはトークンの利用可能性をチェックします(counter> 0)。 トークンが利用できない場合、リクエストは拒否されます。 それ以外の場合、要求は受け入れられて処理され、トークンは使い果たされたと見なされます(カウンターが1つ減ります)。
まあ、未使用のバーストトークンがある場合、NGINXは要求を受け入れます。 しかし、彼はいつそれを処理しますか?
制限を5r / sに設定します。NGINXは、利用可能なバーストトークンがある場合、標準を超える要求を受け入れますが、設定速度に耐えられるように処理を延期します。 つまり、 これらのバースト要求は少し遅れて処理されるか 、タイムアウトになります。
言い換えれば、NGINXはゾーンに設定された制限を超えることはありませんが、追加のリクエストをキューに入れ、それらをいくらか遅延させて処理します。
簡単な例を示します1r/s
制限があり、 burst
が3
ます。 NGINXが一度に5つのリクエストを受信するとどうなりますか?
- 最初のものが受け入れられ、処理されます。
- 許可されるのは1 + 3だけなので、1つの要求はステータスコード503で直ちに拒否されます。
- 残りの3つは1つずつ処理されますが、すぐには処理されません。 NGINXは、
1r/s
速度でそれらをスキップし、確立された制限内に留まります。また、クォータも使用する新しいリクエストがないことを前提としています。 キューが空になると、バーストカウンターが再び増加し始めます(マーカーバスケットがいっぱいになり始めます)。
NGINXがプロキシサーバーとして使用される場合、その背後にあるサービスは1r/s
速度でリクエストを受信し、プロキシサーバーによって平滑化されたトラフィックのバーストについては何も知りません。
したがって、トラフィックシェーピングを構成し、遅延を適用して要求のバーストを制御し、データフローを平滑化しました。
nodelay
nodelay
は、NGINXに、 burst
値で指定されたウィンドウ内のパケットを受け入れ、すぐに処理する必要があることを通知します(通常の要求と同様)。
その結果、トラフィックのバーストは引き続きNGINXの背後にあるサービスに到達しますが、これらのバーストはburst
に制限されます。
リクエスト処理の速度制限の視覚化
練習は何かを思い出すのに大いに役立つと信じているので、NGINXを搭載した小さなDockerイメージを作成しました。 要求処理の速度を制限するためのさまざまなオプションが実装されているリソースが構成されています。基本的な制限、 burst
およびnodelay
を使用した速度制限burst
あります。 それらがどのように機能するかを見てみましょう。
かなり単純なNGINX構成を使用します(これはDockerイメージにもあります。リンクは記事の最後にあります)。
limit_req_zone $request_uri zone=by_uri:10m rate=30r/m; server { listen 80; location /by-uri/burst0 { limit_req zone=by_uri; try_files $uri /index.html; } location /by-uri/burst5 { limit_req zone=by_uri burst=5; try_files $uri /index.html; } location /by-uri/burst5_nodelay { limit_req zone=by_uri burst=5 nodelay; try_files $uri /index.html; } }
要求処理速度を制限するためのさまざまなオプションを備えたNGINXテスト構成
すべてのテストで、この構成を使用して、10個の並列リクエストを同時に送信します。
これを見つけましょう:
- 速度制限のために拒否されるリクエストの数は?
- 受信したリクエストの処理速度はどのくらいですか?
リクエストを処理するための速度制限を使用して、リソースに対して10個の並列リクエストを作成
要求に対する処理の速度制限があるリソースへの10の同時要求
この構成では、1分あたり30のリクエストが許可されています。 ただし、この場合、10のうち9は拒否されます。 前のセクションを注意深く読んだ場合、NGINXのこの動作は驚くことではありません30r/m
は、2秒で1つのリクエストのみが通過することを意味します。 この例では、次の要求を解決するタイマーが機能する前にNGINXに表示されるため、10個の要求が同時に来て、1つがスキップされ、残りの9つが拒否されます。
顧客/エンドポイントのリクエストの小さなスパイクを生き延びます
いいね! 次に、引数burst=5
を追加します。これにより、NGINXは、要求の処理の速度制限を使用して、ゾーンのこのエンドポイントへの要求の小さなバーストをスキップできます。
引数burst = 5の10の同時リソース要求
ここで何が起こったのですか? 予想どおり、 burst
でさらに5つのリクエストが受け入れられ、合計数に対する受け入れられたリクエストの比率が1/10から6/10に改善されました(残りは拒否されました)。 ここで、NGINXがトークンを更新し、受信したリクエストを処理する方法を明確に見ることができます-発信速度は30r/m
制限されます。これは2秒ごとに1つのリクエストに相当します。
最初の要求に対する応答は、0.2秒後に返されます。 タイマーは2秒後に起動し、保留中の要求の1つが処理され、クライアントが応答を受け取ります。 往復に費やされた合計時間は2.02秒でした。 さらに2秒後、タイマーが再び起動し、次の要求を処理できるようになります。合計要求時間は4.02秒で戻ります。 などなど...
したがって、 burst
引数を使用すると、NGINX要求レート制限システムを単純なしきい値フィルターからトラフィックシェーパーに変えることができます。
私のサーバーは余分な負荷を処理できますが、クエリの速度制限を使用して過負荷を防ぎたいと思います
この場合、 nodelay
引数が役立つ場合があります。 burst=5 nodelay
エンドポイントに同じ10個のリクエストを送信しましょう:
引数burst = 5 nodelayの10の同時リソース要求
burst=5
で予想されるように、ステータスコードは200と503の同じ比率になります。 ただし、現在、送信速度は2秒ごとに1つの要求に制限されていません。 バーストトークンが利用可能である限り、着信要求はすぐに受け入れられ、処理されます。 バーストトークンの数を補充するという点では、タイマーの応答速度は依然として重要ですが、遅延は受信した要求には適用されません。
発言。 この場合、 zone
は$request_uri
使用しますが、その後のすべてのテストはbinary_remote_addr
オプションとまったく同じようにbinary_remote_addr
、速度はクライアントIPアドレスによって制限されます。 特別に準備されたDockerイメージを使用して、これらの設定を試す機会があります。
まとめると
NGINXが着信要求を受け入れ、 rate
、 burst
およびnodelay
基づいてそれらを処理する方法を視覚化してみましょう。
複雑にならないように、ゾーン設定で定義されたタイムラインに着信要求の数を表示します(拒否または受け入れられて処理されます)。タイマー操作値の等しいセグメントに分割されます。 時間間隔の絶対値は重要ではありません。 NGINXが各ステップで処理できるリクエストの数は重要です。
以下は、リクエストの処理速度を制限するためのさまざまな設定を介して駆動するトラフィックです。
ゾーンに設定された着信要求と要求処理速度の制限
要求の受け入れと拒否(バースト設定なし)
バーストなし(つまり、 burst=0
)NGINXは速度制限として機能します。 要求はすぐに処理されるか、すぐに拒否されます。
たとえば、設定された制限内の容量をロードするためにトラフィックの小さなバーストを許可する場合、使用可能なバーストトークン内で受信したリクエストの処理の遅延を意味するburst
引数を追加できます。
要求の受け入れ、遅延、拒否(バーストを使用)
拒否されたリクエストの総数が減少していることがわかります。 設定された速度を超える要求のみが拒否され、バーストトークンが利用できない瞬間に到着しました。 これらの設定により、NGINXはトラフィックの完全なシェーピングを実行します。
最後に、NGINXはリクエストのバースト(バースト)のサイズを制限することでトラフィックを制御するために使用できますが、この場合、リクエストのバーストは部分的にプロセッサ(上位またはローカル)に到達するため、最終的には発信速度が安定しなくなりますが、ネットワーク遅延(もちろん、これらの追加リクエストを処理できる場合):
要求の受け入れ、処理、および拒否(nodelayで使用されるバースト)
クエリの速度制限で遊ぶ
ここで、概説した概念の理解をより強固にするために、コードを調べ、リポジトリをコピーし、準備されたDockerイメージを試すことができます。
https://github.com/sportebois/nginx-rate-limit-sandbox
参照:
- オリジナル: NGINXのレート制限の概要 。