フロー制御

フロー制御 (HTTP)

仕様書

フロー制御

[17] HTTP/2接続では複数のストリームを含めることができ、 複数のデータを多重化して並行に送受信することができます。 しかし受信者メモリーなどの資源は有限ですから、 HTTPアプリケーションの処理能力を超えない配慮が必要となります。 フロー制御は、資源の制約があるエンドポイントを保護するためのものです >>1

[2] 接続内で過剰にストリームを使って多重化すると、 他のストリームの処理に干渉して処理しきれなくなるかもしれません >>1

[12] プロキシは多くの接続メモリーを共有する必要がありますし、 上流接続が遅く下流接続が速かったりすることもあります。 >>1

要件

[5] HTTP/2フロー制御は、具体的なアルゴリズムは規定せず送受信者とフレームが満たすべき要件のみを規定しています。 プロトコルを変更せずに適切なフロー制御アルゴリズムを実装できます >>1

[3] フロー制御は、接続全体に関するものと、その中の個別のストリームに関するもので 2段階で行われます >>1, >>23

[13] 受信者があるストリームの処理はできないものの他のストリームは処理したい、 という場合にも対応できます >>1

[6] フロー制御は特定の接続について制御するもので、 単一のホップ (エンドポイント間) に適用されます >>1, >>23利用者エージェントから起源サーバーまでの全体に適用されるものではありません。 中間器は、 WINDOW_UPDATE フレーム転送しません >>23。 (もちろん、間接的に影響を及ぼすことはあります >>23。)

[4] フロー制御には、 WINDOW_UPDATE フレームを使います。 受信者は、ストリームについて、および接続全体について、 受信できるバイト数を広告します。これは credit に基づく方式 (credit-based scheme) です。 >>1

[11] しかし WINDOW_UPDATE フレームをいつ送信するか、 どのように値を決めるか、送信者が送信するかどう判断するかを HTTP/2 仕様としては規定していません。実装は適切なアルゴリズムを実装することができます。 >>1

[8] 新しいストリームおよび接続全体のフロー制御窓の初期値は、 65535 バイトです。 >>1

[7] フロー制御受信者によって制御されます。 受信者ストリームについて、また接続全体について、 任意の窓サイズを設定することができます。 送信者は、受信者によるフロー制御上の制限に従わなければなりません>>1

[10] フロー制御は、無効にはできません。 >>1

[14] フロー制御が必要ない受信者は、データを受信する度に最大の 231-1 のフロー制御窓広告することにより、 実質的にフロー制御を無効化できます。 >>1

[9] フロー制御が適用されるかどうかは、フレーム型に依存します。 フロー制御対象のフレーム (flow-controlled frame) は、 DATA フレームのみです。 >>1, >>23 従って重要な制御フレームがフロー制御によりブロックされることはありません >>1

[16] 帯域遅延積がわからない状態でフロー制御を行うと、 ネットワーク資源を十分使い切れないかもしれません。しかし帯域遅延積がわかっていたとしても、 フロー制御は難しいです。受信者は、 TCP 受信バッファーからデータを随時読み込まなければなりません。 そうしなければ、 WINDOW_UPDATE など重要なフレームを読んで処理することができず、 デッドロックに陥る虞があります。 >>1

[90] ストリームの多重化が存在しない HTTP/1 では、 受信者の用意が整っていなければ、プラットフォームTCP からの受信データの読み出しを行わないことにより、プラットフォーム側で (受信バッファーのデータ長を使った) TCPフロー制御を利用することができました。 ところが HTTP/2 ではあるストリームをそれ以上読む必要が無いとしても、 その後に他のストリームのデータやデータ以外のフレームが続いているかもしれませんから、 常にプラットフォームから受信データを受け取り続ける必要が生じています。

送信者の処理

[15] 送信者は、常に受信者広告したフロー制御窓に従う必要があります。 >>1 送信者は、受信者広告した接続フロー制御窓ストリームフロー制御窓のいずれかを超える長さのフロー制御対象のフレームを送信してはなりません >>23ストリームフロー制御窓の値がの時も、送信してはなりません >>23

[40] 送信者は、 END_STREAM フラグが設定され長さが 0 のフレームなら、 フロー制御窓に空きがなくても送信して構いません >>23

[41] 送信者は、フロー制御対象のフレームを送信したら、 接続フロー制御窓ストリームフロー制御窓から送信したフレームpayload の長さ分を減らします >>23

受信者の処理

[25] フロー制御の適用対象でないフレーム型フレームは、 フロー制御の状態に関わらず受け入れて処理しなければなりません >>23

[26] フロー制御によりフレームを受け入れられないのにフレームを受信したら、 ストリームエラーまたは接続エラー FLOW_CONTROL_ERROR として構いません >>23

[84] サーバーから ChromeFirefox にまとめて大量の DATA を送信しても FLOW_CONTROL_ERROR にはならないようです。

[36] フロー制御対象のフレームを受信したら、 これを接続エラーとして扱う場合を除き、 (エラーである場合も含めて) その接続フロー制御窓への貢献を勘案しなければなりません >>23

[69] closed 状態のストリームDATA フレームを受信した場合でも、接続フロー制御窓には数えます >>64

ストリームの状態遷移の項を参照。

[71] GOAWAY フレームの送信後で無視するストリームDATA フレームを受信した場合、 接続フロー制御窓には数えなければなりません >>70

[42] フロー制御対象のフレーム受信者は、データを消費してフロー制御窓の領域が解放される度に、 WINDOW_UPDATE フレームを送信します。ストリームフロー制御窓接続フロー制御窓について別々に送信します。 >>23

フロー制御窓

[38] 送信者は、 (ウィンドウ) (window) (フロー制御窓 (flow-control window) ) を持ちます。 フロー制御窓には、接続全体の接続フロー制御窓 (connection flow-control window) と、 ストリーム毎のストリームフロー制御窓 (stream flow-control window) があります。 フロー制御窓は、単純な整数値で、送信者が転送できるデータのバイト数です。 これは受信者のバッファリング能力を表しています。 >>23

[39] フロー制御の計算では、フレームヘッダーは数えません >>23payload の長さのみが対象となります。詰めは対象になります。

[49] フロー制御窓のサイズの初期値は、 65535 バイトです >>23

[50] 接続の確立時に、接続フロー制御窓は初期値に設定されます >>23

[51] SETTINGS フレームによる設定の変更の影響は受けません。

[52] ストリームの生成時に、ストリームフロー制御窓は初期値に設定されます >>23

[46] SETTINGS フレームSETTINGS_INITIAL_WINDOW_SIZE 設定で、 初期値が変更されます >>23

[47] 初期値の設定が変更されると、既存のストリームストリームフロー制御窓もその差分に応じて調整しなければなりません >>23。 その結果値がになることもあります >>23

[48] あるいはストリームフロー制御窓は初期値からの差分として保持しておくのも良いかもしれません。
[87] 接続フロー制御窓は、設定の値に影響されません。

[54] フロー制御対象フレーム受信者が現在より小さなフロー制御窓を使いたい時は、 SETTINGS フレームを送信できます。しかし、 その処理前に既にデータが送られている場合がありますから、 受信者は新しいフロー制御窓サイズより大きなデータを受信する準備をしなければなりません受信者SETTINGS フレームを削減した後も、 フロー制御の限界を超えたストリームの処理を継続して構いません。 その場合フロー制御窓に割り当てた領域を直ちに開放することはできません。 WINDOW_UPDATE フレームを送らない限り送信者が送信を再開しないため、 処理が止まってしまう可能性もあります。誤り符号 FLOW_CONTROL_ERRORRST_STREAM フレームを送信することにしても構いません。 >>23

[55] ストリームエラーとはされていませんが、違いがあるのかは不明です。

[61] フロー制御窓の管理方法は、fingerprinting vector です >>60

WINDOW_UPDATE フレーム

意味

[24] WINDOW_UPDATE フレーム (フレーム型 0x8) は、フロー制御の実装に使います >>23

構文

[32] ストリーム識別子は、特定のストリーム識別子を指定することもできますし、 0x0 により接続全体を表すこともできます >>23

[30] フラグはありません >>23。 0 でなければなりません >>63受信者は無視しなければなりません >>63

width
8
  1. 1 0
  2. 1 0
  3. 1 0
  4. 1 0
  5. 1 0
  6. 1 0
  7. 1 0
  8. 1 0

[27] payload は次の欄で構成されます。

[28] R
予約の1ビットのフラグです >>23
[29] 窓サイズ増分 (Window Size Increment)
符号無し31ビット整数 (ネットワークバイト順 >>62) の欄で、 既存のフロー制御窓に加えて送信者が転送できるバイト数を表します >>23合法な範囲は、 1 から 231-1 です >>23

width
32
  1. 1 R
  2. 31 窓サイズ増分

文脈

[34] WINDOW_UPDATE フレームは、 END_STREAM フラグの設定されたフレームを送信したエンドポイントが送信できます。 (つまり half-closed (remote)closedストリームで受信する可能性があります。) >>23

[66] reserved (remote)openhalf-closed (local) で送信できます。

[67] half-closed (local) の時相手はまだデータを送信できる状態ですから、 必要に応じて WINDOW_UPDATE フレームは送らなければなりません >>64

処理

[85] ストリームの状態遷移の項も参照。

[35] 受信者は、 half-closed (remote)closedWINDOW_UPDATE フレームを受信しても、 エラーとしてはなりません >>23

[68] しかし closed に遷移して十分な時間が経つと、エラーとなるかもしれません >>64

[65] half-closed (local) でも受信するかもしれません >>64

[33] 受信者は、フロー制御窓の増分が 0 なら、 ストリームエラー (接続全体に対するものなら接続エラー) PROTOCOL_ERROR としなければなりません >>23

[79] 接続について、FirefoxChrome も仕様通り接続エラー PROTOCOL_ERROR にします。

[37] payload の長さが 4 以外なら、接続エラー FRAME_SIZE_ERROR としなければなりません >>23

[44] フロー制御対象のフレーム送信者は、 フロー制御窓が 231-1 を超えることを認めてはなりません。 そのように求める WINDOW_UPDATE フレームを受信したら、 接続またはストリームを閉じなければなりませんFLOW_CONTROL_ERROR 誤り符号を使って GOAWAY フレームRST_STREAM フレームを送信します。 >>23

[45] なぜか接続エラーストリームエラーとはされていませんが、 違いがあるのかは不明です。
[80] Chrome は接続について上限を超えるとき、 接続エラー PROTOCOL_ERROR を送信します。 ストリームについてはストリームエラー FLOW_CONTROL_ERROR を送信します。Firefox はどちらも仕様通りです。

[43] WINDOW_UPDATE フレームを受信したら、 フロー制御窓を指定に従い更新します >>23

[58] WINDOW_UPDATE フレームは、無駄な処理をさせるために濫用できます。 エンドポイントは利用状況を監視して制限するべきです接続エラー ENHANCE_YOUR_CALM としても構いません。 >>59

設定

[19] 設定 SETTINGS_INITIAL_WINDOW_SIZE (0x4) は、 送信者ストリームレベルのフロー制御初期窓サイズ (initial window size) バイト単位で表します >>18

[21] 設定は、すべてのストリームで共通です。

[20] 初期値は、 216-1 です >>18

[22] 最大フロー制御窓サイズである 231-1 より大きな値は、 接続エラー FLOW_CONTROL_ERROR としなければなりません >>18

[53] いずれかのフロー制御窓の値が最大サイズを超えるように SETTINGS_INITIAL_WINDOW_SIZE を変更しようとした場合は、 接続エラー FLOW_CONTROL_ERROR としなければなりません >>23

[86] ChromeFirefox も、こうした場合に (少なくても) 接続エラーにはしていないようです。

誤り符号 FLOW_CONTROL_ERROR

[57] 誤り符号 FLOW_CONTROL_ERROR (0x3) は、 peerフロー制御プロトコルに違反していることを示します >>56

実装

[75] 具体的な動作はプラットフォームメモリー利用状況などで異なるかもしれません。

[72] Chrome接続序文SETTINGS で、 SETTINGS_INITIAL_WINDOW_SIZE を 10485760 = 223 + 221 に設定します。

[73] Chrome接続序文直後に WINDOW_UPDATE で値 10420225 = 223 + 221 - (216 - 1) を送信するようです。

[82] Chrome は一定量の (をかなり使うくらいの) DATA を受信するごとに、そのストリームについての WINDOW_UPDATE で 3153920 = 221 + 220 + 213 を送るようです。

[83] Chrome はそれに加えて (より少ない頻度で) 接続についての WINDOW_UPDATE で 7872512 = 222 + 221 + 220 + 219 + 213 を送るようです。

[74] Firefox接続序文SETTINGS で、 SETTINGS_INITIAL_WINDOW_SIZE を 131072 = 216 × 2 に設定します。

[76] Firefox接続序文直後に WINDOW_UPDATE で値 268369921 = 228 - (216 - 1) を送信するようです。

[77] FirefoxHEADERS の直後に、そのストリームについて WINDOW_UPDATE で値 268304384 = 228 - 216 × 2 を送信するようです。

[81] Firefox は一定量の (をかなり使うくらいの) DATA を受信するごとに、 そのストリームについての WINDOW_UPDATE と、 接続についての WINDOW_UPDATE でそれぞれ 4194304 = 222 ずつ増やすようです。

[88] #959 (Permit post before acking settings) – nginx ( ()) https://trac.nginx.org/nginx/ticket/959

[89] ios - NSURLErrorDomain Code=-1004 for few seconds after app start up - Stack Overflow ( ()) http://stackoverflow.com/questions/36907767/nsurlerrordomain-code-1004-for-few-seconds-after-app-start-up