概要
で説明したように、Circuit Breakerのパラメータ調整は大変です。
実際のキャパシティよりパラメータを小さくしてしまうと、リソースはまだ余裕があるのにOpenしていまいます。
逆にパラメータが大きすぎると、Openが遅れサービス側に過負荷がかかってしまいます。
また一度適切に設定してからも、実装によってキャパシティが変更されればそれに合わせてCircuit Breaker側も変更が必要になります。
そのようなCircuit Breakerの課題を解決するものとしてAdaptive Concurrencyというものがあるので紹介します。
環境
- Envoy v1.22.0
Adaptive Concurrency Filter
概念
Concurrencyは並行して処理可能な数ですが、それを超えるリクエストの場合はqueueに入れられます。
なので同時に受け付けることのできるin-flight requestsは
となります。
in-flight requestsのキャパシティ計算にはリトルの法則を用いるのが一般的です。
スループットはスケールアップやスケールアウトなどでシステムを増強しない限り固定なので、in-flight requestsが増えれば増えるほどリクエストは段々と捌けなくなりレイテンシ(RTT)が悪化します。
レイテンシが悪化が進めばリクエストはどんどんTimeoutします。そして最終的には詰まったリクエストによってOOMなどが発生します。
そこでin-flight requestsの流量を、レイテンシの変化に基づいて動的に制限しようというのがAdaptive Concurrencyです。
gradient
以下の式からレイテンシの変化をgradientとして定期的にチェックします。
負荷がないときの値÷現在値で出すということですね。
Envoyでは
- RTT_noload = minRTT
- RTT_actual = sampleRTT
と表現しています。
このgradientの値がどう変化するかによって、limitの値を変えていきます。
gradient値 | 状態 |
---|---|
1 | queueに余裕がありlimitを増やすことができる |
1未満 | 過剰なqueueが形成されたため、limitを減らす必要がある |
具体的な式は以下のようになります。
headroomは制限がどれだけ速く増加するかを決定するために使用されます。
Envoyでは調整できません。
Envoyの設定
すでにfilterが用意されているので、それをHTTP Filter Chainに追加します。
http_filters: - name: envoy.filters.http.adaptive_concurrency typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.adaptive_concurrency.v3.AdaptiveConcurrency gradient_controller_config: sample_aggregate_percentile: value: 90 concurrency_limit_params: concurrency_update_interval: 0.1s min_rtt_calc_params: jitter: value: 10 min_concurrency: 30 interval: 30s request_count: 25 enabled: default_value: true runtime_key: "adaptive_concurrency.enabled" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
パラメータ
触りそうなパラメータについて説明します。
パラメータ | 説明 |
---|---|
concurrency_update_interval | sampleRTTを収集してLimitを更新するインターバル |
sample_aggregate_percentile | ↑の期間で収集されたレイテンシのpercentileを使用 |
min_rtt_calc_params.min_concurrency | minRTTとsampleRTTで計算されるconcurrency limitの下限を設定する。 concurrencyが小さくなりすぎる問題を防ぐ |
min_rtt_calc_params.interval | minRTTを再計算するインターバル |
min_rtt_calc_params.jitter | minRTTの再計算を開始する際のランダムな遅延 |
min_rtt_calc_params.request_count | request_count分リクエストサンプルを収集してminRTTを計算する。p90を使用する |
動作検証
シナリオ
以下のシナリオで負荷をかけてみます。
結果
20rpsで一定に返していることが分かります。
そしてシナリオ通り途中からリクエストが増え、キャパシティを超えたことでdurationが急増していることが分かります。
しかしAdaptive Concurrency Filterにより、リクエストが遮断され503が返ることで、backend serviceに過負荷がかからないようになっています。
前述の計算式から並行で捌けるリクエストを絞っていることが確認できます。
その他
サンプルコード
今回の検証コードはこちら
KubeCon + CloudNativeCon Europe 2020での解説動画
こちらの動画で分かりやすく説明されています。
まとめ
Circuit Breakerの課題であるパラメータ調整を、レイテンシに応じて自動的に調整できるAdaptive Concurrencyを検証してみました。
IstioおよびAnthos Service Meshではまだ使えませんが、利用できるようになったらぜひ導入したいですね。
参考
- Performance Under Load. Adaptive Concurrency Limits @ Netflix | by Netflix Technology Blog | Medium
- Adaptive Concurrency — envoy 1.23.0-dev-469f7b documentation
- Using Little’s Law to Measure System Performance - Kevin Sookocheff
- performance - What does "in-flight request" mean for a web browser? - Stack Overflow