Kong Gatewayを使うと流量制限が出来るのは知ってる人は知ってる話だが、これまではリクエスト数が一定数を超えると429 Too Many Requests
を返すという機能のみ提供されており、帯域幅に基づくスロットリングはサポートされていなかった。
10月にリリースされたKong Gatewayのv3.12に付随するRate Limiting Advanced Pluginではスロットリングがサポートされ、429ステータスコードで即座に拒否されるのではなく、遅延して再試行することが出来るようになった。
これを実際に試してみた。
Rate Limiting Advanced Pluginの設定
スロットリングに関する以下の4つのパラメータが追加されている。
パラメータ | 意味 |
---|---|
config.throttling.enabled |
スロットリングを有効にするかどうか。デフォルトはfalse
|
config.throttling.interval |
スロットリングされた個々のリクエストの再試行間隔。デフォルトは5
|
config.throttling.queue_limit |
スロットリングキューの最大長。デフォルトは5
|
config.throttling.retry_times |
スロットリングされたリクエストの再試行回数。デフォルトは3
|
発動条件についてはこれまで通りwindow_size
とlimit
で設定する。
limit
を超えた分は一旦キューに入れられ、interval
で指定された秒数ごとにキューから取り出されて処理される。
スロットリングを設定しているにも関わらず429を返すのは以下のケースで、1つはキューから溢れる場合で、queue_limit
を超えてリクエストを受け取ると429が返される。
キューの上限は1000000なので、キュー溢れを許さないような場合は適切に設定したい。
もう1つはリトライ回数を超えた場合。
window_size
に対してinterval
が極端に小さい場合は単一リクエストでも429が返されることがあるので注意が必要そう。
検証
今回は以下のようなdeck用のYAMLを用意して試してみた。
_format_version: "3.0"
services:
- enabled: true
host: httpbin.konghq.com
name: httpbin-service
path: /
port: 443
protocol: https
routes:
- name: httpbin-route
paths:
- /httpbin
plugins:
- config:
limit:
- 3
namespace: namespace
strategy: local
throttling:
enabled: true
interval: 5
queue_limit: 5
retry_times: 3
window_size:
- 10
window_type: sliding
enabled: true
name: rate-limiting-advanced
ServiceとしてKong社がホスティングしているhttpbinを指定し、Routeのパスには/httpbin
を指定している。
Rate Limiting Advanced PluginをRouteにアタッチし、10秒間に3リクエストを超えた場合にスロットリングが発動するように設定している。
設定後検証を行う。以下のようにして短期間に10回連続でリクエストを送り、ステータスコードとKong本体のレイテンシを確認する
for((i=0;i<10;i++)) ; do curl -s localhost:8000/httpbin -I |grep "HTTP\|X-Kong-Proxy-Latency" ; done
上手く動作すれば、4回目以降のアクセスがエラーは返らないものの遅くなるはずだ。
実際に実行した結果は以下のような感じになる。
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 2
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 2
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 4
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 5007
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 5007
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 5006
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 3
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 5001
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 5006
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 5006
4回目以降、X-Kong-Proxy-Latency
が5秒以上になっているのが分かる。
これにより、429ステータスコードを返すことなく、リクエストが遅延されていることが分かる。
また7回目のレイテンシは3msになっているが、これは流量制限の解除秒数が経過したためだと考えられる。
次に429ステータスコードが返されるケースを試してみる。
429を返すのはキューから溢れた時かリトライ回数を超えたときなので、まずはキューから溢れるケースを試してみる。
キューの上限を1に変更し、2つのターミナルから同時に先程のfor文を実行する。
ターミナル1の実行結果は以下。
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 3
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
:(省略)
ターミナル2の実行結果は以下。
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 1
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 1
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 15011
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
;(省略)
4回目以降の実施で、キューに入ったものだけ遅延して送られ、それ以外のものは429が返されているのが分かる。
なお、キューから削除されるのがレスポンスを受け取って、次のリクエストを送った後っぽく、ターミナルを複数用意しなくても単一のfor文でもキュー数が1であれば再現できる。
(バグのような気がするのでそのうち修正されるかも)
次にリトライ回数を超えた場合を試してみる。
単一の実行でリトライ数を超えるにはおおよそwindow_size
>interval
*retry_times
を満たせばよいので、ここでは余裕を持たせてwindow_size
を30にして確認してみる(キューサイズは元の3に戻す)。
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 1
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 2
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 0
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 15011
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 15009
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 15009
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 15007
HTTP/1.1 200 OK
X-Kong-Proxy-Latency: 15005
期待した通り、4回目で429が返されているのが分かる。
ただ、6回目以降は遅延しているものの429は返されていない。
これはある程度時間が経つことでwindow_size
範囲内のリクエスト数がlimit
を下回り、再びリクエストが通るようになったためと考えられる。
所感
今回の検証により、Rate Limiting Advanced Pluginでスロットリングがサポートされたことが確認できた。
リクエスト数超過時に拒否はせずにサービスの質を落として継続したいケースもあると思う。
この機能追加によりその要望にも使えるようになったのは有り難い。
一方で、帯域幅の指定とかが出来るわけではないので、パラメタチューニングはやや難度が高いかと感じた。
使いこなすにはある程度テストとかを繰り返す必要がありそう。