Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C# と HTTP/2 と gRPC

neno
March 25, 2023

C# と HTTP/2 と gRPC

.NET ラボ 2023/03/25 での発表資料

ブログ :
- C# と HTTP/2 と gRPC
- HTTP/2 と gRPC に対するよくある誤解。

neno

March 25, 2023
Tweet

More Decks by neno

Other Decks in Technology

Transcript

  1. 自己紹介 1 • 所属: NTTコミュニケーションズ イノベーションセンター • 趣味: C#, OSS,

    ドール, 一眼(α7 IV) • 執心領域 • C# ⇔ TypeScript • SignalR 何縫ねの。 nenoNaninu nenoMake ブログ https://blog.neno.dev その他 https://neno.dev
  2. OSS 紹介 2 属性を付与するだけ Tapper • C# の型定義から TypeScript の型定義を生成する

    .NET Tool/ library • JSON / MessagePack 対応! https://github.com/nenoNaninu/Tapper
  3. OSS 紹介 3 • C# の SignalR Client を強く型付けするための Source

    Generator TypedSignalR.Client Before After (using TypedSignalR.Client) こんな SignalR の Hub と Receiver の interface が あったとして… 脱文字列! 全てが強く型付け! https://github.com/nenoNaninu/TypedSignalR.Client
  4. 4 • TypeScript の SignalR Client を強く型付けするための .NET Tool /

    library TypedSignalR.Client.TypeScript Before After (using TypedSignalR.Client.TypeScript) 脱文字列! 全てが強く型付け! TypeScript 用の型を C# から自動生成 MessagePack Hub Protocol 対応! https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript 属性を付与するだけ! OSS 紹介
  5. OSS 紹介 5 • SignalR 使ったアプリを快適に開発するための GUI を自動生成する library •

    2 step で利用可能! • http pipeline に middleware の追加 • Hub と Receiver を定義してる interface に属性を付与 • JWT 認証 サポート • パラメータのユーザ定義型サポート • JSON で入力! SignalR 版 SwaggerUI TypedSignalR.Client.DevTools https://github.com/nenoNaninu/TypedSignalR.Client.DevTools
  6. HTTP/2 の基本 22 • RFC 7540 : HTTP/2 の最初の RFC

    • RFC 8740 : HTTP/2 を TLS 1.3 で使うための RFC • RFC 9113 : 2022年2月に新たに作られた RFC。現行の HTTP/2 の仕様。 HTTP/2 に関する RFC の歴史 https://www.rfc-editor.org/rfc/rfc9113
  7. HTTP/2 の基本 23 • RFC 7540 : HTTP/2 の最初の RFC

    • RFC 8740 : HTTP/2 を TLS 1.3 で使うための RFC • RFC 9113 : 2022年2月に新たに作られた RFC。現行の HTTP/2 の仕様。 HTTP/2 に関する RFC の歴史 https://www.rfc-editor.org/rfc/rfc9113
  8. HTTP/2 の基本 24 • RFC 7540 : HTTP/2 の最初の RFC

    • RFC 8740 : HTTP/2 を TLS 1.3 で使うための RFC • RFC 9113 : 2022年2月に新たに作られた RFC。現行の HTTP/2 の仕様。 HTTP/2 に関する RFC の歴史 https://www.rfc-editor.org/rfc/rfc9113 注) このスライド中には 分かりやすさのため、 RFC 7540 から図を引用 してる場合があります。
  9. TCP TCP TCP HTTP/2 の基本 25 • HTTP/1.x HTTP/2 からリクエスト(ストリーム)が多重化された

    • HTTP/2 server client server client /hoge.json /fuga.json /piyo.json TCP Stream 1 /hoge .json Stream 3 /huga .json Stream 5 /piyo .json Stream 1 /hoge .json Stream 5 /piyo .json Stream 3 /huga .json
  10. HTTP/2 の基本 26 • 当然、フレーミング。 • TCP とかがやっているような事を HTTP/2 のレイヤーでもやるようになった、

    と思えばスッと入ってくる。ハズ。 • HTTP/1.x はフレーミングは行わず、素朴にテキストをそのまま connection に投げ込んでいた。 多重化するためには何が必要? https://www.rfc-editor.org/rfc/rfc7540#section-4.1 https://www.rfc-editor.org/rfc/rfc9113#section-4.1 header payload
  11. HTTP/2 の基本 27 • Length • ペイロードの長さ • 24 bit

    確保されてるけど、SETTINGS_MAX_FRAME_SIZE を 設定しない限り 214 (=16,384) を超えてはいけない。 • Type • Payload がどんな種別かを示す。 • Flag • Frame Type にそれぞれ異なるフラグが定義されている。 • R • Reserved Bit。 使われてない。 • Stream Identifier • 31 bit の符号なし整数 全フレーム共通の固定長 header https://www.rfc-editor.org/rfc/rfc7540#section-4.1 https://www.rfc-editor.org/rfc/rfc7540#section-11.2
  12. HTTP/2 の基本 28 • Length • ペイロードの長さ • 24 bit

    確保されてるけど、SETTINGS_MAX_FRAME_SIZE を 設定しない限り 214 (=16,384) を超えてはいけない。 • Type • Payload がどんな種別かを示す。 • Flag • Frame Type にそれぞれ異なるフラグが定義されている。 • R • Reserved Bit。 使われてない。 • Stream Identifier • 31 bit の符号なし整数 全フレーム共通の固定長 header https://www.rfc-editor.org/rfc/rfc7540#section-4.1 https://www.rfc-editor.org/rfc/rfc7540#section-11.2
  13. HTTP/2 の Stream 30 • ストリームとは… • それぞれ独立している • 双方向なフレームの

    sequence • 頭に入れておきたい特徴 • 一つの HTTP/2 接続の中に、同時に複数の ストリームを開け、各 endpoint は複数の ストリームからフレームを接続に差し込める • 特定のストリームを処理している事を理由に、 他のストリームがコネクションにフレームを 差し込めない(≒書き込めない) という事は(仕様上)ない。 • ストリーム内でのフレームの順序は大事 • Stream は integer によって識別される HTTP/2 における stream とは? https://www.rfc-editor.org/rfc/rfc9113#section-5 https://www.rfc-editor.org/rfc/rfc9113#section-2.2 1 request 1 stream
  14. HTTP/2 の Stream 31 • Http2Stream という class が内部的には存在する。 •

    が、Http2Connection という internal class に含まれる private class なので触る事はできない。 • 普通に HTTP/2 を使う上では全く気にしなくていいところなので当然といえば当然。 C# 的には?
  15. HTTP/2 の Stream 32 • Stream を作成するたびに発行される。 • Client 側が発行できる

    stream id は奇数 • Server 側が発行できる stream id は偶数 • 基本的に server push を利用する場合に使われる。 • Stream id が 0 の stream は制御用ストリーム • 新しいストリームが作成されるごとに(つまり、リクエスト毎に) それぞれ +2 されていく。 • ストリームは使い捨て。再利用できない。 Stream Identifier server client Stream 1 /hoge .json Stream 3 /huga .json Stream 5 /piyo .json
  16. HTTP/2 の Stream 34 • Http2Connection (internal class) のコンストラクタを眺める。 •

    標準ライブラリに含まれる実装。つまり client 側。 • _nextStream = 1 で奇数。 Stream Identifier
  17. HTTP/2 の Stream 35 • ストリームが追加された時、どんなことやってるか。 • SendAsync から追ってみる。 •

    SendHeadersAsync が Http2stream のインスタンスを返している。 Stream Identifier
  18. HTTP/2 の Stream 36 • ストリームが追加された時、どんなことやってるか。 • SendAsync から追ってみる。 •

    SendHeadersAsync が Http2stream のインスタンスを返している。 Stream Identifier
  19. HTTP/2 の Stream 40 Stream Identifier Stream id が 渡されている

    親切なコメントと共に +2 されてる。
  20. HTTP/2 の Stream 41 Stream Identifier _nextStream が max の時

    Shutdown() される 親切なコメントと共に +2 されてる。 Stream id が 渡されている
  21. HTTP/2 の Stream 42 Stream Identifier _nextStream が max の時

    Shutdown() される Shutdown() 内部で _shutdown が true になる 親切なコメントと共に +2 されてる。 Stream id が 渡されている
  22. HTTP/2 の Stream 43 Stream Identifier _nextStream が max の時

    Shutdown() される Shutdown() 内部で _shutdown が true になる 例外が投げられる 親切なコメントと共に +2 されてる。 Stream id が 渡されている
  23. HTTP/2 の Stream 44 Stream Identifier _nextStream が max の時

    Shutdown() される Shutdown() 内部で _shutdown が true になる 例外が投げられる 親切なコメントと共に +2 されてる。 Stream id が 渡されている
  24. HTTP/2 の Stream 45 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…?
  25. HTTP/2 の Stream 46 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…? while(true)
  26. HTTP/2 の Stream 47 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…? while(true) Http2Connection.SendAsync
  27. HTTP/2 の Stream 48 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…? while(true) Http2Connection.SendAsync
  28. HTTP/2 の Stream 49 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…? while(true) Http2Connection.SendAsync AddStream で投げた 例外を catch
  29. HTTP/2 の Stream 50 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…? while(true) Http2Connection.SendAsync AddStream で投げた 例外を catch リトライの回数を 超えたら throw
  30. HTTP/2 の Stream 51 • HttpConnectionPool (internal class) の SendWithVersionDetectionAndRetryAsync

    メソッドに行きつく。 Stream Id が枯渇した時、Retry はどこで…? while(true) Http2Connection.SendAsync AddStream で投げた 例外を catch Eat exception (while true なので retry) リトライの回数を 超えたら throw
  31. HTTP/2 のフレーム 53 • Length • ペイロードの長さ • 24 bit

    確保されてるけど、SETTINGS_MAX_FRAME_SIZE を 設定しない限り 214 (=16,384) を超えてはいけない。 • Type • Payload がどんな種別かを示す。 • Flag • Frame Type にそれぞれ異なるフラグが定義されている。 • R • Reserved Bit。 使われてない。 • Stream Identifier • 31 bit の符号なし整数 全フレーム共通の固定長 header https://www.rfc-editor.org/rfc/rfc7540#section-4.1 https://www.rfc-editor.org/rfc/rfc7540#section-11.2
  32. HTTP/2 のフレーム 54 • Length • ペイロードの長さ • 24 bit

    確保されてるけど、SETTINGS_MAX_FRAME_SIZE を 設定しない限り 214 (=16,384) を超えてはいけない。 • Type • Payload がどんな種別かを示す。 • Flag • Frame Type にそれぞれ異なるフラグが定義されている。 • R • Reserved Bit。 使われてない。 • Stream Identifier • 31 bit の符号なし整数 全フレーム共通の固定長 header https://www.rfc-editor.org/rfc/rfc7540#section-4.1 https://www.rfc-editor.org/rfc/rfc7540#section-11.2
  33. HTTP/2 のフレーム 55 • Length • ペイロードの長さ • 24 bit

    確保されてるけど、SETTINGS_MAX_FRAME_SIZE を 設定しない限り 214 (=16,384) を超えてはいけない。 • Type • Payload がどんな種別かを示す。 • Flag • Frame Type にそれぞれ異なるフラグが定義されている。 • R • Reserved Bit。 使われてない。 • Stream Identifier • 31 bit の符号なし整数 全フレーム共通の固定長 header https://www.rfc-editor.org/rfc/rfc7540#section-4.1 https://www.rfc-editor.org/rfc/rfc7540#section-11.2 Type 一覧
  34. HTTP/2 のフレーム 56 • POST 等で送るサーバに送るデータや、レスポンスのコンテンツは DATA Frame に詰め込まれる •

    Flag • END_STREAM (0x1) • フレームがストリームの終端の場合に立つフラグ。 • PADDED (0x8) DATA Frame (0x0) https://www.rfc-editor.org/rfc/rfc9113#section-6.1
  35. HTTP/2 のフレーム 57 • GET リクエストだとヘッダーフレームだけ飛ぶことが殆ど。 • Flag • END_STREAM

    (0x1) • 後ろに CONTINUATION Frame 以外のフレーム (DATA Frame 等) が 続かない場合に立つフラグ。 • END_HEADERS(0x4) • ヘッダーが 1 フレームに収まる場合に立つフラグ。 • 収まらなかった場合 CONTINUATION フレームが後ろに続く。 • PADDED (0x8) • PRIORITY (0x20) HEADERS Frame (0x1) https://www.rfc-editor.org/rfc/rfc9113#section-6.2
  36. HTTP/2 のフレーム 58 GET の(殆どの)場合 header type: HEADERS header type:

    DATA payload server client payload Flag: END_STREAM END_HEADERS payload header type: HEADERS
  37. HTTP/2 のフレーム 59 POST の(殆どの)場合 header type: DATA header type:

    DATA payload server client payload payload header type: HEADERS header type: HEADERS payload Flag: END_HEADERS Flag: END_STREAM ・・・
  38. HTTP/2 のフレーム 60 • サーバとクライアントそれぞれで最初に送信するフレーム。 • コネクションレベルの設定を行う。 • Flag •

    ACK (0x1) • Identifier • SETTINGS_HEADER_TABLE_SIZE (0x1) • SETTINGS_ENABLE_PUSH (0x2) • SETTINGS_MAX_CONCURRENT_STREAMS (0x3) • SETTINGS_INITIAL_WINDOW_SIZE (0x4) • SETTINGS_MAX_FRAME_SIZE (0x5) • SETTINGS_MAX_HEADER_LIST_SIZE (0x6) SETTINGS Frame (0x4) https://www.rfc-editor.org/rfc/rfc9113#section-6.5
  39. HTTP/2 のフレーム 61 • サーバとクライアントそれぞれで最初に送信するフレーム。 • コネクションレベルの設定を行う。 • Flag •

    ACK (0x1) • Identifier • SETTINGS_HEADER_TABLE_SIZE (0x1) • SETTINGS_ENABLE_PUSH (0x2) • SETTINGS_MAX_CONCURRENT_STREAMS (0x3) • SETTINGS_INITIAL_WINDOW_SIZE (0x4) • SETTINGS_MAX_FRAME_SIZE (0x5) • SETTINGS_MAX_HEADER_LIST_SIZE (0x6) SETTINGS Frame (0x4) https://www.rfc-editor.org/rfc/rfc9113#section-6.5 flow control の設定
  40. HTTP/2 のフレーム 62 • サーバとクライアントそれぞれで最初に送信するフレーム。 • コネクションレベルの設定を行う。 • Flag •

    ACK (0x1) • Identifier • SETTINGS_HEADER_TABLE_SIZE (0x1) • SETTINGS_ENABLE_PUSH (0x2) • SETTINGS_MAX_CONCURRENT_STREAMS (0x3) • SETTINGS_INITIAL_WINDOW_SIZE (0x4) • SETTINGS_MAX_FRAME_SIZE (0x5) • SETTINGS_MAX_HEADER_LIST_SIZE (0x6) SETTINGS Frame (0x4) https://www.rfc-editor.org/rfc/rfc9113#section-6.5 どっちが先に送ってもOK flow control の設定
  41. HTTP/2 のフレーム 63 • サーバとクライアントそれぞれで最初に送信するフレーム。 • コネクションレベルの設定を行う。 • Flag •

    ACK (0x1) • Identifier • SETTINGS_HEADER_TABLE_SIZE (0x1) • SETTINGS_ENABLE_PUSH (0x2) • SETTINGS_MAX_CONCURRENT_STREAMS (0x3) • SETTINGS_INITIAL_WINDOW_SIZE (0x4) • SETTINGS_MAX_FRAME_SIZE (0x5) • SETTINGS_MAX_HEADER_LIST_SIZE (0x6) SETTINGS Frame (0x4) SETTINGS フレームを受信し 受信した側がその設定を反映できたら Length=0 (payload 無し), Flag= 0x1 で SETTINGS Frame を送る https://www.rfc-editor.org/rfc/rfc9113#section-6.5 どっちが先に送ってもOK flow control の設定
  42. HTTP/2 のフレーム 64 • C# / Standard library (client-side) で設定可能な値

    • SETTINGS_INITIAL_WINDOW_SIZE のみ。 SETTINGS Frame (0x4)
  43. HTTP/2 のフレーム 67 • 1つの Connection で幾つストリームを同時に実行できるかの設定。 • ブラウザの場合… •

    HTTP/1.x • TCP を 1 本以上接続して、TCP のコネクション分同時にリクエスト • コネクション数以上のリクエストは1度キューに入って順次実行 • HTTP/2 • TCP を 1 本接続して、SETTINGS_MAX_CONCURRENT_STREAMS で設定された数だけ 同時にリクエスト • SETTINGS_MAX_CONCURRENT_STREAMS 以上のリクエストが必要な場合は、 一度キューに入って順次実行。 SETTINGS_MAX_CONCURRENT_STREAMS https://www.rfc-editor.org/rfc/rfc9113#section-6.5.2
  44. HTTP/2 のフレーム 68 • 1つの Connection で幾つストリームを同時に実行できるかの設定。 • ブラウザの場合… •

    HTTP/1.x • TCP を 1 本以上接続して、TCP のコネクション分同時にリクエスト • コネクション数以上のリクエストは1度キューに入って順次実行 • HTTP/2 • TCP を 1 本接続して、SETTINGS_MAX_CONCURRENT_STREAMS で設定された数だけ 同時にリクエスト • SETTINGS_MAX_CONCURRENT_STREAMS 以上のリクエストが必要な場合は、 一度キューに入って順次実行。 SETTINGS_MAX_CONCURRENT_STREAMS https://www.rfc-editor.org/rfc/rfc9113#section-6.5.2 殆どのブラウザは同一オリジンに対して TCP の接続を同時に 6 本開く
  45. HTTP/2 のフレーム 69 • 1つの Connection で幾つストリームを同時に実行できるかの設定。 • ブラウザの場合… •

    HTTP/1.x • TCP を 1 本以上接続して、TCP のコネクション分同時にリクエスト • コネクション数以上のリクエストは1度キューに入って順次実行 • HTTP/2 • TCP を 1 本接続して、SETTINGS_MAX_CONCURRENT_STREAMS で設定された数だけ 同時にリクエスト • SETTINGS_MAX_CONCURRENT_STREAMS 以上のリクエストが必要な場合は、 一度キューに入って順次実行。 SETTINGS_MAX_CONCURRENT_STREAMS https://www.rfc-editor.org/rfc/rfc9113#section-6.5.2 殆どのブラウザは同一オリジンに対して TCP の接続を同時に 6 本開く 100 か 128 が殆ど
  46. HTTP/2 のフレーム 70 • 1つの Connection で幾つストリームを同時に実行できるかの設定。 • ブラウザの場合… •

    HTTP/1.x • TCP を 1 本以上接続して、TCP のコネクション分同時にリクエスト • コネクション数以上のリクエストは1度キューに入って順次実行 • HTTP/2 • TCP を 1 本接続して、SETTINGS_MAX_CONCURRENT_STREAMS で設定された数だけ 同時にリクエスト • SETTINGS_MAX_CONCURRENT_STREAMS 以上のリクエストが必要な場合は、 一度キューに入って順次実行。 SETTINGS_MAX_CONCURRENT_STREAMS https://www.rfc-editor.org/rfc/rfc9113#section-6.5.2 HTTP/1.x に比べて圧倒的改善 殆どのブラウザは同一オリジンに対して TCP の接続を同時に 6 本開く 100 か 128 が殆ど
  47. HTTP/2 のフレーム 72 • Micro service 間通信の場合… • SETTINGS_MAX_CONCURRENT_STREAMS はデフォルトで

    100 とか 128 が多い。 • Micro service 間通信で 100 リクエスト以上同時に実行する事は普通。 • TCP 1本で接続して、同時に開くストリーム数が SETTINGS_MAX_CONCURRENT_STREAMS で設定されている数を超えた場合、 キューに積まれて待機させられる。 SETTINGS_MAX_CONCURRENT_STREAMS https://www.rfc-editor.org/rfc/rfc9113#section-6.5
  48. HTTP/2 のフレーム 73 • Micro service 間通信の場合… • SETTINGS_MAX_CONCURRENT_STREAMS はデフォルトで

    100 とか 128 が多い。 • Micro service 間通信で 100 リクエスト以上同時に実行する事は普通。 • TCP 1本で接続して、同時に開くストリーム数が SETTINGS_MAX_CONCURRENT_STREAMS で設定されている数を超えた場合、 キューに積まれて待機させられる。 SETTINGS_MAX_CONCURRENT_STREAMS つまり、ボトルネックになってしまう https://www.rfc-editor.org/rfc/rfc9113#section-6.5
  49. HTTP/2 のフレーム 74 • Micro service 間通信の場合… • SETTINGS_MAX_CONCURRENT_STREAMS はデフォルトで

    100 とか 128 が多い。 • Micro service 間通信で 100 リクエスト以上同時に実行する事は普通。 • TCP 1本で接続して、同時に開くストリーム数が SETTINGS_MAX_CONCURRENT_STREAMS で設定されている数を超えた場合、 キューに積まれて待機させられる。 SETTINGS_MAX_CONCURRENT_STREAMS つまり、ボトルネックになってしまう 当然、解決策は用意されている。 https://www.rfc-editor.org/rfc/rfc9113#section-6.5
  50. HTTP/2 のフレーム 75 • EnableMultipleHttp2Connections • 同時に開いている stream が SETTINGS_MAX_CONCURRENT_STREAMS

    に 達した場合、新しい HTTP/2 connection が作成される。 SETTINGS_MAX_CONCURRENT_STREAMS https://github.com/dotnet/runtime/issues/35088 https://github.com/dotnet/runtime/pull/39439
  51. HTTP/2 のフレーム 76 • Micro service 間の通信で HTTP/2 を使う場合、gRPC が殆ど。

    • GrpcChannel に HttpHandler を設定しないといけない? • 不要。 SETTINGS_MAX_CONCURRENT_STREAMS この構成のためだけに handler を new して option として渡す必要はない
  52. HTTP/2 のフレーム 77 • grpc-dotnet 内部で EnableMultipleHttp2Connections を true にしてくれている。

    • ForAddress だけで問題なし SETTINGS_MAX_CONCURRENT_STREAMS https://github.com/grpc/grpc-dotnet/blob/v2.52.x/src/Shared/HttpHandlerFactory.cs
  53. HTTP/2 のフレーム 78 • その名の通り疎通確認するためのフレーム。 • クライアントからでもサーバからでも飛ばしてOK • PING Frame

    が送られたら優先度高く PING Frame を送り返す必要がある。 • Flag • ACK (0x1) • 応答の PING Frame には ACK フラグが立つ。 PING Frame (0x6) https://www.rfc-editor.org/rfc/rfc9113#section-6.7
  54. HTTP/2 のフレーム 79 • Q : HTTP のレイヤーに必要? • A

    : 必要。 • 特に gRPC で streaming を使っている場合、必須。 PING Frame (0x6) https://www.rfc-editor.org/rfc/rfc9113#section-6.7 server client Firewall, Load Balancer, etc.
  55. HTTP/2 のフレーム 80 • Q : HTTP のレイヤーに必要? • A

    : 必要。 • 特に gRPC で streaming を使っている場合、必須。 PING Frame (0x6) https://www.rfc-editor.org/rfc/rfc9113#section-6.7 server client Firewall, Load Balancer, etc. 一定時間何もデータが 流れてこなかったらコネクションを 切断してしまう
  56. HTTP/2 のフレーム 81 • Q : HTTP のレイヤーに必要? • A

    : 必要。 • 特に gRPC で streaming を使っている場合、必須。 PING Frame (0x6) https://www.rfc-editor.org/rfc/rfc9113#section-6.7 server client Firewall, Load Balancer, etc. 一定時間何もデータが 流れてこなかったらコネクションを 切断してしまう 結果、streaming でいざデータを 流そうとした時に失敗する
  57. HTTP/2 のフレーム 82 • Q : HTTP のレイヤーに必要? • A

    : 必要。 • 特に gRPC で streaming を使っている場合、必須。 PING Frame (0x6) https://www.rfc-editor.org/rfc/rfc9113#section-6.7 server client Firewall, Load Balancer, etc. 一定時間何もデータが 流れてこなかったらコネクションを 切断してしまう 結果、streaming でいざデータを 流そうとした時に失敗する PING フレームで 接続を監視しつつ維持
  58. HTTP/2 のフレーム 83 • C# / Standard library (client-side) で設定可能な値

    • KeepAlivePingPolicy • Always: 常に一定間隔で PING フレームを飛ばす • WithActiveRequests : 開いてるストリームがある場合のみ PING フレームを飛ばす • gRPC で streaming 使ってる場合のみ PING 飛ばしたかったらこっち。 PING Frame (0x6) https://github.com/dotnet/runtime/issues/31198 https://github.com/dotnet/runtime/pull/40257
  59. HTTP/2 のフレーム 84 • C# / ASP.NET Core (server-side) で設定可能な値

    • client-side と違って、設定したら http2 connection 内に 有効な stream が無くても問答無用で PING Frame が送られる。 • KeepAlivePingPolicy のような気の利いたオプションは無い。(現時点では) PING Frame (0x6)
  60. Application-Layer Protocol Negotiation 87 • h2の場合、HTTP/1.x と HTTP/2 を 1つのポートで

    listen する事ができる。 • h2c の場合、HTTP/1.x とHTTP/2 を それぞれ別の port で listen する必要がある。 • h2c を用いる場合は、server-side に以下のような設定を行う必要がある。 appsettings.json 等に記述 C# で直接記述 h2 か h2c かで port 事情が変わってくる
  61. Application-Layer Protocol Negotiation 88 https://www.rfc-editor.org/rfc/rfc7301 なぜこのような違いが? • アプリケーション層のプロトコルの選択 (HTTP/1.x か

    HTTP/2 か) を TLS のレイヤで行っているから、h2 は 1 つの port で OK. • Application-Layer Protocol Negotiation (ALPN) のお仕事。 • TLS の仕事は暗号化だけではないのです…。 • h2c はアプリケーション層のプロトコルの選択ができないので port を分ける他ない。
  62. Application-Layer Protocol Negotiation 89 https://www.rfc-editor.org/rfc/rfc7301 ALPN の流れを非常にざっくり解説すると… • TLS の処理の流れの一部は以下のような事を行う。

    • ①TLS では、client は ClientHello で自分が扱える暗号化方式等の リストを server に渡す • ②server は ServerHello で実際の接続で利用する暗号化方式等を client に 渡す • ClientHello / ServerHelloに 暗号化方式等だけでなく、 アプリケーション層の プロトコル選択も含ませたのが ALPN。
  63. Application-Layer Protocol Negotiation 91 https://www.rfc-editor.org/rfc/rfc7301 世のホスティングサービスは app の前段で TLS 終端するよな…?

    • Azure App Service / Cloud Run etc. • エンドユーザからのリクエストは https • Load balancer とかで TLS 終端 • App が動いてる VM / Container に届くリクエストは http (not https) client Load Balancer etc. https app http
  64. Application-Layer Protocol Negotiation 92 https://www.rfc-editor.org/rfc/rfc7301 世のホスティングサービスは app の前段で TLS 終端するよな…?

    • Azure App Service / Cloud Run etc. • エンドユーザからのリクエストは https • Load balancer とかで TLS 終端 • App が動いてる VM / Container に届くリクエストは http (not https) client Load Balancer etc. TLS 終端 https app http
  65. Application-Layer Protocol Negotiation 93 https://www.rfc-editor.org/rfc/rfc7301 世のホスティングサービスは app の前段で TLS 終端するよな…?

    • Azure App Service / Cloud Run etc. • エンドユーザからのリクエストは https • Load balancer とかで TLS 終端 • App が動いてる VM / Container に届くリクエストは http (not https) client Load Balancer etc. TLS 終端 https ALPN が使える app http
  66. Application-Layer Protocol Negotiation 94 https://www.rfc-editor.org/rfc/rfc7301 世のホスティングサービスは app の前段で TLS 終端するよな…?

    • Azure App Service / Cloud Run etc. • エンドユーザからのリクエストは https • Load balancer とかで TLS 終端 • App が動いてる VM / Container に届くリクエストは http (not https) client Load Balancer etc. TLS 終端 https ALPN が使える ALPN が使えない…! app http
  67. Application-Layer Protocol Negotiation 98 https://github.com/dotnet/aspnetcore/issues/9020 Azure App Service は割とつい最近 gRPC

    が public preview に • 今まで何で出来なかったのか? • 2019/04 から ASP.NET Core にある issue (~2022/08) • IIS / HTTP.Sys がうんぬんかんぬん。 • 詳しくは極めて長いこの issue を読んでください…
  68. Application-Layer Protocol Negotiation 103 https://github.com/microsoft/reverse-proxy https://www.infoq.com/news/2022/01/microsoft-releases-yarp/ https://techcommunity.microsoft.com/t5/apps-on-azure-blog/a-heavy-lift-bringing-kestrel-yarp-to-azure-app-services/ba-p/3607417 どうして出来るようになった? • IIS

    が YARP に完全に置き換わった • App Service Worker が Linux だろうと、その前端に IIS が今まではいた。 FrontEndRole が IIS から YARP に この子が TLS 終端も担っている 我々の app が 動いている VM
  69. Application-Layer Protocol Negotiation 105 https://www.rfc-editor.org/rfc/rfc7301 結果的に以下のようになったので end-to-end で HTTP/2 が使えるように。

    client YARP TLS 終端 https ALPN が使える ALPN が使えない…! だけど別々の port に リバプロで捌けば 問題なし! app Port 8080 Port 9999
  70. Application-Layer Protocol Negotiation 106 https://github.com/dotnet/aspnetcore/issues/9020#issuecomment-566172172 https://cloud.google.com/run/docs/configuring/http2 Azure App Service、昔から HTTP/2

    サポートしてなかったけ? • 実は end-to-end で HTTP/2 が使えていたわけではない。 • クライアントからのリクエストは HTTP/2 でも… • App の VM に届く前のタイミングで HTTP/1.1 に downgrade されていた。 • これだと gRPC は使えない。 • Cloud Run も既定はこのスタイル。 client IIS https HTTP/1.1 に downgrade! app http HTTP/1.x or HTTP/2
  71. まとめ 107 • HttpClient / HttpMessageInvoker / SocketsHttpHandler • 何か設定したくなったら

    SocketsHttpHandler を確認! • Stream / stream identifier • stream id の枯渇とかは気にしないで OK. • Frame type • DATA Frame / HEADERS Frame • SETTINGS Frame / PING Frame • HTTP/2 特有の connection の設定/維持/監視などのお話 • C# (standard lib, ASP.NET Core, grpc-dotnet) で気を付けるポイントを紹介 • Application-Layer Protocol Negotiation • h2c (= w/o TLS) の場合に何故 port を分けないといけないのか • Azure App Service が gRPC をサポートしたが、その裏がどうなっているのか HTTP/3 の仕様は RFC 9114 で確定済 しかし HTTP/2 はこれからも使われる & HTTP/2 を知らないと HTTP/3 も 理解できないので頭に片隅に https://www.rfc-editor.org/rfc/rfc9114.html