.NET ラボ 2023/03/25 での発表資料
ブログ : - C# と HTTP/2 と gRPC - HTTP/2 と gRPC に対するよくある誤解。
C# と HTTP/2 と gRPC.NET ラボ 2023/03/25何縫ねの。
View Slide
自己紹介1• 所属: NTTコミュニケーションズイノベーションセンター• 趣味: C#, OSS, ドール, 一眼(α7 IV)• 執心領域• C# ⇔ TypeScript• SignalR何縫ねの。nenoNaninunenoMakeブログ https://blog.neno.devその他 https://neno.dev
OSS 紹介2属性を付与するだけTapper• C# の型定義から TypeScript の型定義を生成する .NET Tool/ library• JSON / MessagePack 対応!https://github.com/nenoNaninu/Tapper
OSS 紹介3• C# の SignalR Client を強く型付けするための Source GeneratorTypedSignalR.ClientBeforeAfter (using TypedSignalR.Client)こんな SignalR のHub と Receiver の interface があったとして…脱文字列!全てが強く型付け!https://github.com/nenoNaninu/TypedSignalR.Client
4• TypeScript の SignalR Client を強く型付けするための .NET Tool / libraryTypedSignalR.Client.TypeScriptBeforeAfter (using TypedSignalR.Client.TypeScript)脱文字列!全てが強く型付け!TypeScript 用の型をC# から自動生成MessagePack Hub Protocol 対応!https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript属性を付与するだけ!OSS 紹介
OSS 紹介5• SignalR 使ったアプリを快適に開発するための GUI を自動生成する library• 2 step で利用可能!• http pipeline に middleware の追加• Hub と Receiver を定義してるinterface に属性を付与• JWT 認証 サポート• パラメータのユーザ定義型サポート• JSON で入力!SignalR 版 SwaggerUITypedSignalR.Client.DevToolshttps://github.com/nenoNaninu/TypedSignalR.Client.DevTools
今回の講演の注意事項!6• 細かいコードがしばしば出てきますが、登壇後にこのスライドをアップするので気になる方は是非、という趣旨で載せています。• 講演中は、細かいコードについては「ふ~ん」と雰囲気をつかめれば OK くらいの感覚で喋っています。
HTTP/2 の基本7
の前に HttpClient の基本8
HttpClient の基本9• HttpClient のコンストラクタ意識するべき登場人物たち
HttpClient の基本10• HttpClient のコンストラクタ意識するべき登場人物たち
HttpClient の基本11• HttpClient のコンストラクタ意識するべき登場人物たちHttpMessageHandler はシンプルな abstract class
HttpClient の基本12意識するべき登場人物たち• HttpClientHandler の中身
HttpClient の基本13意識するべき登場人物たち• HttpClientHandler の中身
HttpClient の基本14意識するべき登場人物たち• HttpClientHandler の中身SocketsHttpHandlerこの子が大事
HttpClient の基本15HttpMessageHandlerを継承している意識するべき登場人物たち• HttpClientHandler の中身SocketsHttpHandlerこの子が大事
HttpClient の基本16意識するべき登場人物たちSocketsHttpHandlerに対して HTTP/2 の設定とかを行う
HttpClient の基本17• HttpClient のベースクラスに注目。意識するべき登場人物たち
HttpClient の基本18• HttpClient のベースクラスに注目。意識するべき登場人物たち
HttpClient の基本19意識するべき登場人物たちSendAsync しか使わないならHttpMessageInvoker で十分
HttpClient の基本20意識するべき登場人物たちSendAsync しか使わないならHttpMessageInvoker で十分実際、GrpcChannel の既定の構成ではHttpClient ではなく、HttpMessageInvoker が用いられる
HTTP/2 の基本21
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
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
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 から図を引用してる場合があります。
TCPTCPTCPHTTP/2 の基本25• HTTP/1.xHTTP/2 からリクエスト(ストリーム)が多重化された• HTTP/2serverclient serverclient/hoge.json/fuga.json/piyo.jsonTCPStream 1/hoge.jsonStream 3/huga.jsonStream 5/piyo.jsonStream 1/hoge.jsonStream 5/piyo.jsonStream 3/huga.json
HTTP/2 の基本26• 当然、フレーミング。• TCP とかがやっているような事を HTTP/2 のレイヤーでもやるようになった、と思えばスッと入ってくる。ハズ。• HTTP/1.x はフレーミングは行わず、素朴にテキストをそのまま connection に投げ込んでいた。多重化するためには何が必要?https://www.rfc-editor.org/rfc/rfc7540#section-4.1https://www.rfc-editor.org/rfc/rfc9113#section-4.1header payload
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 の符号なし整数全フレーム共通の固定長 headerhttps://www.rfc-editor.org/rfc/rfc7540#section-4.1https://www.rfc-editor.org/rfc/rfc7540#section-11.2
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 の符号なし整数全フレーム共通の固定長 headerhttps://www.rfc-editor.org/rfc/rfc7540#section-4.1https://www.rfc-editor.org/rfc/rfc7540#section-11.2
Stream29
HTTP/2 の Stream30• ストリームとは…• それぞれ独立している• 双方向なフレームの sequence• 頭に入れておきたい特徴• 一つの HTTP/2 接続の中に、同時に複数のストリームを開け、各 endpoint は複数のストリームからフレームを接続に差し込める• 特定のストリームを処理している事を理由に、他のストリームがコネクションにフレームを差し込めない(≒書き込めない)という事は(仕様上)ない。• ストリーム内でのフレームの順序は大事• Stream は integer によって識別されるHTTP/2 における stream とは?https://www.rfc-editor.org/rfc/rfc9113#section-5https://www.rfc-editor.org/rfc/rfc9113#section-2.21 request1 stream
HTTP/2 の Stream31• Http2Stream という class が内部的には存在する。• が、Http2Connection という internal class に含まれる private class なので触る事はできない。• 普通に HTTP/2 を使う上では全く気にしなくていいところなので当然といえば当然。C# 的には?
HTTP/2 の Stream32• Stream を作成するたびに発行される。• Client 側が発行できる stream id は奇数• Server 側が発行できる stream id は偶数• 基本的に server push を利用する場合に使われる。• Stream id が 0 の stream は制御用ストリーム• 新しいストリームが作成されるごとに(つまり、リクエスト毎に)それぞれ +2 されていく。• ストリームは使い捨て。再利用できない。Stream IdentifierserverclientStream 1/hoge.jsonStream 3/huga.jsonStream 5/piyo.json
HTTP/2 の Stream33Stream Identifier31 bit 分枯渇したらどうなる??
HTTP/2 の Stream34• Http2Connection (internal class) のコンストラクタを眺める。• 標準ライブラリに含まれる実装。つまり client 側。• _nextStream = 1 で奇数。Stream Identifier
HTTP/2 の Stream35• ストリームが追加された時、どんなことやってるか。• SendAsync から追ってみる。• SendHeadersAsync が Http2stream のインスタンスを返している。Stream Identifier
HTTP/2 の Stream36• ストリームが追加された時、どんなことやってるか。• SendAsync から追ってみる。• SendHeadersAsync が Http2stream のインスタンスを返している。Stream Identifier
HTTP/2 の Stream37• new Http2Stream() してる箇所ではなく、AddStream されているところで stream id は渡されている。Stream Identifier
HTTP/2 の Stream38• new Http2Stream() してる箇所ではなく、AddStream されているところで stream id は渡されている。Stream Identifier
HTTP/2 の Stream39Stream Identifier親切なコメントと共に +2されてる。
HTTP/2 の Stream40Stream IdentifierStream id が渡されている親切なコメントと共に +2されてる。
HTTP/2 の Stream41Stream Identifier _nextStream が max の時Shutdown() される親切なコメントと共に +2されてる。Stream id が渡されている
HTTP/2 の Stream42Stream Identifier _nextStream が max の時Shutdown() されるShutdown() 内部で_shutdown が true になる親切なコメントと共に +2されてる。Stream id が渡されている
HTTP/2 の Stream43Stream Identifier _nextStream が max の時Shutdown() されるShutdown() 内部で_shutdown が true になる例外が投げられる親切なコメントと共に +2されてる。Stream id が渡されている
HTTP/2 の Stream44Stream Identifier _nextStream が max の時Shutdown() されるShutdown() 内部で_shutdown が true になる例外が投げられる親切なコメントと共に +2されてる。Stream id が渡されている
HTTP/2 の Stream45• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?
HTTP/2 の Stream46• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?while(true)
HTTP/2 の Stream47• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?while(true)Http2Connection.SendAsync
HTTP/2 の Stream48• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?while(true)Http2Connection.SendAsync
HTTP/2 の Stream49• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?while(true)Http2Connection.SendAsyncAddStream で投げた例外を catch
HTTP/2 の Stream50• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?while(true)Http2Connection.SendAsyncAddStream で投げた例外を catchリトライの回数を超えたら throw
HTTP/2 の Stream51• HttpConnectionPool (internal class) のSendWithVersionDetectionAndRetryAsync メソッドに行きつく。Stream Id が枯渇した時、Retry はどこで…?while(true)Http2Connection.SendAsyncAddStream で投げた例外を catchEat exception(while true なので retry)リトライの回数を超えたら throw
Frame Type52
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 の符号なし整数全フレーム共通の固定長 headerhttps://www.rfc-editor.org/rfc/rfc7540#section-4.1https://www.rfc-editor.org/rfc/rfc7540#section-11.2
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 の符号なし整数全フレーム共通の固定長 headerhttps://www.rfc-editor.org/rfc/rfc7540#section-4.1https://www.rfc-editor.org/rfc/rfc7540#section-11.2
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 の符号なし整数全フレーム共通の固定長 headerhttps://www.rfc-editor.org/rfc/rfc7540#section-4.1https://www.rfc-editor.org/rfc/rfc7540#section-11.2Type 一覧
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
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
HTTP/2 のフレーム58GET の(殆どの)場合headertype: HEADERSheadertype: DATApayloadserverclientpayloadFlag: END_STREAMEND_HEADERSpayloadheadertype: HEADERS
HTTP/2 のフレーム59POST の(殆どの)場合headertype: DATAheadertype: DATApayloadserverclientpayloadpayloadheadertype: HEADERSheadertype: HEADERSpayloadFlag: END_HEADERSFlag: END_STREAM・・・
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
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.5flow controlの設定
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どっちが先に送ってもOKflow controlの設定
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どっちが先に送ってもOKflow controlの設定
HTTP/2 のフレーム64• C# / Standard library (client-side) で設定可能な値• SETTINGS_INITIAL_WINDOW_SIZE のみ。SETTINGS Frame (0x4)
HTTP/2 のフレーム65• C# / ASP.NET Core (server-side) で設定可能な値SETTINGS Frame (0x4)
HTTP/2 のフレーム66• C# / ASP.NET Core (server-side) で設定可能な値SETTINGS Frame (0x4)これに注目
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_STREAMShttps://www.rfc-editor.org/rfc/rfc9113#section-6.5.2
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_STREAMShttps://www.rfc-editor.org/rfc/rfc9113#section-6.5.2殆どのブラウザは同一オリジンに対してTCP の接続を同時に 6 本開く
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_STREAMShttps://www.rfc-editor.org/rfc/rfc9113#section-6.5.2殆どのブラウザは同一オリジンに対してTCP の接続を同時に 6 本開く100 か 128 が殆ど
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_STREAMShttps://www.rfc-editor.org/rfc/rfc9113#section-6.5.2HTTP/1.x に比べて圧倒的改善殆どのブラウザは同一オリジンに対してTCP の接続を同時に 6 本開く100 か 128 が殆ど
HTTP/2 のフレーム71Micro service 間は?
HTTP/2 のフレーム72• Micro service 間通信の場合…• SETTINGS_MAX_CONCURRENT_STREAMS はデフォルトで 100 とか 128 が多い。• Micro service 間通信で 100 リクエスト以上同時に実行する事は普通。• TCP 1本で接続して、同時に開くストリーム数がSETTINGS_MAX_CONCURRENT_STREAMS で設定されている数を超えた場合、キューに積まれて待機させられる。SETTINGS_MAX_CONCURRENT_STREAMShttps://www.rfc-editor.org/rfc/rfc9113#section-6.5
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
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
HTTP/2 のフレーム75• EnableMultipleHttp2Connections• 同時に開いている stream が SETTINGS_MAX_CONCURRENT_STREAMS に達した場合、新しい HTTP/2 connection が作成される。SETTINGS_MAX_CONCURRENT_STREAMShttps://github.com/dotnet/runtime/issues/35088https://github.com/dotnet/runtime/pull/39439
HTTP/2 のフレーム76• Micro service 間の通信で HTTP/2 を使う場合、gRPC が殆ど。• GrpcChannel に HttpHandler を設定しないといけない?• 不要。SETTINGS_MAX_CONCURRENT_STREAMSこの構成のためだけにhandler を new してoption として渡す必要はない
HTTP/2 のフレーム77• grpc-dotnet 内部で EnableMultipleHttp2Connections をtrue にしてくれている。• ForAddress だけで問題なしSETTINGS_MAX_CONCURRENT_STREAMShttps://github.com/grpc/grpc-dotnet/blob/v2.52.x/src/Shared/HttpHandlerFactory.cs
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
HTTP/2 のフレーム79• Q : HTTP のレイヤーに必要?• A : 必要。• 特に gRPC で streaming を使っている場合、必須。PING Frame (0x6)https://www.rfc-editor.org/rfc/rfc9113#section-6.7serverclientFirewall,Load Balancer,etc.
HTTP/2 のフレーム80• Q : HTTP のレイヤーに必要?• A : 必要。• 特に gRPC で streaming を使っている場合、必須。PING Frame (0x6)https://www.rfc-editor.org/rfc/rfc9113#section-6.7serverclientFirewall,Load Balancer,etc.一定時間何もデータが流れてこなかったらコネクションを切断してしまう
HTTP/2 のフレーム81• Q : HTTP のレイヤーに必要?• A : 必要。• 特に gRPC で streaming を使っている場合、必須。PING Frame (0x6)https://www.rfc-editor.org/rfc/rfc9113#section-6.7serverclientFirewall,Load Balancer,etc.一定時間何もデータが流れてこなかったらコネクションを切断してしまう結果、streaming でいざデータを流そうとした時に失敗する
HTTP/2 のフレーム82• Q : HTTP のレイヤーに必要?• A : 必要。• 特に gRPC で streaming を使っている場合、必須。PING Frame (0x6)https://www.rfc-editor.org/rfc/rfc9113#section-6.7serverclientFirewall,Load Balancer,etc.一定時間何もデータが流れてこなかったらコネクションを切断してしまう結果、streaming でいざデータを流そうとした時に失敗するPING フレームで接続を監視しつつ維持
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/31198https://github.com/dotnet/runtime/pull/40257
HTTP/2 のフレーム84• C# / ASP.NET Core (server-side) で設定可能な値• client-side と違って、設定したら http2 connection 内に有効な stream が無くても問答無用で PING Frame が送られる。• KeepAlivePingPolicy のような気の利いたオプションは無い。(現時点では)PING Frame (0x6)
Application-LayerProtocol Negotiation85
Application-Layer Protocol Negotiation86• まずは単語の定義からhttps://www.rfc-editor.org/rfc/rfc7540#section-11.1
Application-Layer Protocol Negotiation87• 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 事情が変わってくる
Application-Layer Protocol Negotiation88https://www.rfc-editor.org/rfc/rfc7301なぜこのような違いが?• アプリケーション層のプロトコルの選択 (HTTP/1.x か HTTP/2 か) をTLS のレイヤで行っているから、h2 は 1 つの port で OK.• Application-Layer Protocol Negotiation (ALPN) のお仕事。• TLS の仕事は暗号化だけではないのです…。• h2c はアプリケーション層のプロトコルの選択ができないのでport を分ける他ない。
Application-Layer Protocol Negotiation89https://www.rfc-editor.org/rfc/rfc7301ALPN の流れを非常にざっくり解説すると…• TLS の処理の流れの一部は以下のような事を行う。• ①TLS では、client は ClientHello で自分が扱える暗号化方式等のリストを server に渡す• ②server は ServerHello で実際の接続で利用する暗号化方式等を client に渡す• ClientHello / ServerHelloに暗号化方式等だけでなく、アプリケーション層のプロトコル選択も含ませたのがALPN。
Application-Layer Protocol Negotiation90ちょっと待てよ?
Application-Layer Protocol Negotiation91https://www.rfc-editor.org/rfc/rfc7301世のホスティングサービスは app の前段で TLS 終端するよな…?• Azure App Service / Cloud Run etc.• エンドユーザからのリクエストは https• Load balancer とかで TLS 終端• App が動いてる VM / Container に届くリクエストは http (not https)clientLoadBalanceretc.httpsapphttp
Application-Layer Protocol Negotiation92https://www.rfc-editor.org/rfc/rfc7301世のホスティングサービスは app の前段で TLS 終端するよな…?• Azure App Service / Cloud Run etc.• エンドユーザからのリクエストは https• Load balancer とかで TLS 終端• App が動いてる VM / Container に届くリクエストは http (not https)clientLoadBalanceretc.TLS 終端httpsapphttp
Application-Layer Protocol Negotiation93https://www.rfc-editor.org/rfc/rfc7301世のホスティングサービスは app の前段で TLS 終端するよな…?• Azure App Service / Cloud Run etc.• エンドユーザからのリクエストは https• Load balancer とかで TLS 終端• App が動いてる VM / Container に届くリクエストは http (not https)clientLoadBalanceretc.TLS 終端httpsALPN が使えるapphttp
Application-Layer Protocol Negotiation94https://www.rfc-editor.org/rfc/rfc7301世のホスティングサービスは app の前段で TLS 終端するよな…?• Azure App Service / Cloud Run etc.• エンドユーザからのリクエストは https• Load balancer とかで TLS 終端• App が動いてる VM / Container に届くリクエストは http (not https)clientLoadBalanceretc.TLS 終端httpsALPN が使える ALPN が使えない…!apphttp
Application-Layer Protocol Negotiation95HTTP/2 使えないのか…?
Application-Layer Protocol Negotiation96そんな事はない
Application-Layer Protocol Negotiation97https://azure.microsoft.com/en-us/updates/public-preview-grpc-support-in-azure-app-service/Azure App Service は割とつい最近 gRPC が public preview に• 2022/09/14 の記事• ただし Linux のみ。
Application-Layer Protocol Negotiation98https://github.com/dotnet/aspnetcore/issues/9020Azure App Service は割とつい最近 gRPC が public preview に• 今まで何で出来なかったのか?• 2019/04 から ASP.NET Core にある issue (~2022/08)• IIS / HTTP.Sys がうんぬんかんぬん。• 詳しくは極めて長いこの issue を読んでください…
Application-Layer Protocol Negotiation99https://github.com/microsoft/reverse-proxyhttps://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 に完全に置き換わった• 2022/08/24 の記事YARP : C# で実装された新しいリバースプロキシ(kestrel ベース)
Application-Layer Protocol Negotiation100https://github.com/microsoft/reverse-proxyhttps://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 が今まではいた。
Application-Layer Protocol Negotiation101https://github.com/microsoft/reverse-proxyhttps://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 が今まではいた。我々の app が動いている VM
Application-Layer Protocol Negotiation102https://github.com/microsoft/reverse-proxyhttps://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 に我々の app が動いている VM
Application-Layer Protocol Negotiation103https://github.com/microsoft/reverse-proxyhttps://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
Application-Layer Protocol Negotiation104https://www.rfc-editor.org/rfc/rfc7301結果的に以下のようになったので end-to-end でHTTP/2 が使えるように。client YARPTLS 終端httpsALPN が使えるappPort 8080Port 9999
Application-Layer Protocol Negotiation105https://www.rfc-editor.org/rfc/rfc7301結果的に以下のようになったので end-to-end でHTTP/2 が使えるように。client YARPTLS 終端httpsALPN が使えるALPN が使えない…!だけど別々の port にリバプロで捌けば 問題なし!appPort 8080Port 9999
Application-Layer Protocol Negotiation106https://github.com/dotnet/aspnetcore/issues/9020#issuecomment-566172172https://cloud.google.com/run/docs/configuring/http2Azure App Service、昔から HTTP/2 サポートしてなかったけ?• 実は end-to-end で HTTP/2 が使えていたわけではない。• クライアントからのリクエストは HTTP/2 でも…• App の VM に届く前のタイミングで HTTP/1.1 に downgrade されていた。• これだと gRPC は使えない。• Cloud Run も既定はこのスタイル。client IIShttpsHTTP/1.1 に downgrade!apphttpHTTP/1.x or HTTP/2
まとめ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