Slide 1

Slide 1 text

C# と HTTP/2 と gRPC .NET ラボ 2023/03/25 何縫ねの。

Slide 2

Slide 2 text

自己紹介 1 • 所属: NTTコミュニケーションズ イノベーションセンター • 趣味: C#, OSS, ドール, 一眼(α7 IV) • 執心領域 • C# ⇔ TypeScript • SignalR 何縫ねの。 nenoNaninu nenoMake ブログ https://blog.neno.dev その他 https://neno.dev

Slide 3

Slide 3 text

OSS 紹介 2 属性を付与するだけ Tapper • C# の型定義から TypeScript の型定義を生成する .NET Tool/ library • JSON / MessagePack 対応! https://github.com/nenoNaninu/Tapper

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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 紹介

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

今回の講演の注意事項! 6 • 細かいコードがしばしば出てきますが、登壇後にこのスライドを アップするので気になる方は是非、という趣旨で載せています。 • 講演中は、細かいコードについては「ふ~ん」と雰囲気を つかめれば OK くらいの感覚で喋っています。

Slide 8

Slide 8 text

HTTP/2 の基本 7

Slide 9

Slide 9 text

の前に HttpClient の基本 8

Slide 10

Slide 10 text

HttpClient の基本 9 • HttpClient のコンストラクタ 意識するべき登場人物たち

Slide 11

Slide 11 text

HttpClient の基本 10 • HttpClient のコンストラクタ 意識するべき登場人物たち

Slide 12

Slide 12 text

HttpClient の基本 11 • HttpClient のコンストラクタ 意識するべき登場人物たち HttpMessageHandler は シンプルな abstract class

Slide 13

Slide 13 text

HttpClient の基本 12 意識するべき登場人物たち • HttpClientHandler の中身

Slide 14

Slide 14 text

HttpClient の基本 13 意識するべき登場人物たち • HttpClientHandler の中身

Slide 15

Slide 15 text

HttpClient の基本 14 意識するべき登場人物たち • HttpClientHandler の中身 SocketsHttpHandler この子が大事

Slide 16

Slide 16 text

HttpClient の基本 15 HttpMessageHandler を継承している 意識するべき登場人物たち • HttpClientHandler の中身 SocketsHttpHandler この子が大事

Slide 17

Slide 17 text

HttpClient の基本 16 意識するべき登場人物たち SocketsHttpHandler に対して HTTP/2 の 設定とかを行う

Slide 18

Slide 18 text

HttpClient の基本 17 • HttpClient のベースクラスに注目。 意識するべき登場人物たち

Slide 19

Slide 19 text

HttpClient の基本 18 • HttpClient のベースクラスに注目。 意識するべき登場人物たち

Slide 20

Slide 20 text

HttpClient の基本 19 意識するべき登場人物たち SendAsync しか使わないなら HttpMessageInvoker で十分

Slide 21

Slide 21 text

HttpClient の基本 20 意識するべき登場人物たち SendAsync しか使わないなら HttpMessageInvoker で十分 実際、GrpcChannel の 既定の構成では HttpClient ではなく、 HttpMessageInvoker が 用いられる

Slide 22

Slide 22 text

HTTP/2 の基本 21

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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 から図を引用 してる場合があります。

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Stream 29

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

HTTP/2 の Stream 31 • Http2Stream という class が内部的には存在する。 • が、Http2Connection という internal class に含まれる private class なので触る事はできない。 • 普通に HTTP/2 を使う上では全く気にしなくていいところなので当然といえば当然。 C# 的には?

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

HTTP/2 の Stream 33 Stream Identifier 31 bit 分枯渇したらどうなる??

Slide 35

Slide 35 text

HTTP/2 の Stream 34 • Http2Connection (internal class) のコンストラクタを眺める。 • 標準ライブラリに含まれる実装。つまり client 側。 • _nextStream = 1 で奇数。 Stream Identifier

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

HTTP/2 の Stream 37 • new Http2Stream() してる箇所ではなく、 AddStream されているところで stream id は渡されている。 Stream Identifier

Slide 39

Slide 39 text

HTTP/2 の Stream 38 • new Http2Stream() してる箇所ではなく、 AddStream されているところで stream id は渡されている。 Stream Identifier

Slide 40

Slide 40 text

HTTP/2 の Stream 39 Stream Identifier 親切なコメントと共に +2 されてる。

Slide 41

Slide 41 text

HTTP/2 の Stream 40 Stream Identifier Stream id が 渡されている 親切なコメントと共に +2 されてる。

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

Frame Type 52

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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 一覧

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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 ・・・

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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 の設定

Slide 63

Slide 63 text

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 の設定

Slide 64

Slide 64 text

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 の設定

Slide 65

Slide 65 text

HTTP/2 のフレーム 64 • C# / Standard library (client-side) で設定可能な値 • SETTINGS_INITIAL_WINDOW_SIZE のみ。 SETTINGS Frame (0x4)

Slide 66

Slide 66 text

HTTP/2 のフレーム 65 • C# / ASP.NET Core (server-side) で設定可能な値 SETTINGS Frame (0x4)

Slide 67

Slide 67 text

HTTP/2 のフレーム 66 • C# / ASP.NET Core (server-side) で設定可能な値 SETTINGS Frame (0x4) これに注目

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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 本開く

Slide 70

Slide 70 text

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 が殆ど

Slide 71

Slide 71 text

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 が殆ど

Slide 72

Slide 72 text

HTTP/2 のフレーム 71 Micro service 間は?

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

HTTP/2 のフレーム 76 • Micro service 間の通信で HTTP/2 を使う場合、gRPC が殆ど。 • GrpcChannel に HttpHandler を設定しないといけない? • 不要。 SETTINGS_MAX_CONCURRENT_STREAMS この構成のためだけに handler を new して option として渡す必要はない

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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.

Slide 81

Slide 81 text

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. 一定時間何もデータが 流れてこなかったらコネクションを 切断してしまう

Slide 82

Slide 82 text

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 でいざデータを 流そうとした時に失敗する

Slide 83

Slide 83 text

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 フレームで 接続を監視しつつ維持

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

HTTP/2 のフレーム 84 • C# / ASP.NET Core (server-side) で設定可能な値 • client-side と違って、設定したら http2 connection 内に 有効な stream が無くても問答無用で PING Frame が送られる。 • KeepAlivePingPolicy のような気の利いたオプションは無い。(現時点では) PING Frame (0x6)

Slide 86

Slide 86 text

Application-Layer Protocol Negotiation 85

Slide 87

Slide 87 text

Application-Layer Protocol Negotiation 86 • まずは単語の定義から https://www.rfc-editor.org/rfc/rfc7540#section-11.1

Slide 88

Slide 88 text

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 事情が変わってくる

Slide 89

Slide 89 text

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 を分ける他ない。

Slide 90

Slide 90 text

Application-Layer Protocol Negotiation 89 https://www.rfc-editor.org/rfc/rfc7301 ALPN の流れを非常にざっくり解説すると… • TLS の処理の流れの一部は以下のような事を行う。 • ①TLS では、client は ClientHello で自分が扱える暗号化方式等の リストを server に渡す • ②server は ServerHello で実際の接続で利用する暗号化方式等を client に 渡す • ClientHello / ServerHelloに 暗号化方式等だけでなく、 アプリケーション層の プロトコル選択も含ませたのが ALPN。

Slide 91

Slide 91 text

Application-Layer Protocol Negotiation 90 ちょっと待てよ?

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

Application-Layer Protocol Negotiation 95 HTTP/2 使えないのか…?

Slide 97

Slide 97 text

Application-Layer Protocol Negotiation 96 そんな事はない

Slide 98

Slide 98 text

Application-Layer Protocol Negotiation 97 https://azure.microsoft.com/en-us/updates/public-preview-grpc-support-in-azure-app-service/ Azure App Service は割とつい最近 gRPC が public preview に • 2022/09/14 の記事 • ただし Linux のみ。

Slide 99

Slide 99 text

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 を読んでください…

Slide 100

Slide 100 text

Application-Layer Protocol Negotiation 99 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 に完全に置き換わった • 2022/08/24 の記事 YARP : C# で実装された 新しいリバースプロキシ (kestrel ベース)

Slide 101

Slide 101 text

Application-Layer Protocol Negotiation 100 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 が今まではいた。

Slide 102

Slide 102 text

Application-Layer Protocol Negotiation 101 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 が今まではいた。 我々の app が 動いている VM

Slide 103

Slide 103 text

Application-Layer Protocol Negotiation 102 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 に 我々の app が 動いている VM

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

Application-Layer Protocol Negotiation 104 https://www.rfc-editor.org/rfc/rfc7301 結果的に以下のようになったので end-to-end で HTTP/2 が使えるように。 client YARP TLS 終端 https ALPN が使える app Port 8080 Port 9999

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

まとめ 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