MagicOnion で gRPC の keepalive 周りの設定を行う方法

# はじめに

MagicOnion の StreamingHub を利用している際にクライアント側でインターネットの接続を切っても、
サーバ側からクライアントの終了を即時に検知することが出来ないでいました。。

僕の環境ではその挙動でも問題がなかったので放置していたのですが、
他にも悩んでいる方がいるようだったので、調査することにしました :mag: 

その結果、どうやら gRPC の keepalive の設定を行うことで対策出来そうでした :thumbsup: 
今後も keepalive の設定をすることはありそうなので記事として残すことにしました :pencil: 

# 動作環境

- .NET Core サーバ
    - ASP.NET Core 2.2
    - [MagicOnion 2.1.0(2.1.2)](https://github.com/Cysharp/MagicOnion/releases/tag/2.1.0)

- Unity クライアント
    - Unity 2019.1.4f1
    - [MagicOnion 2.1.0(2.1.2)](https://github.com/Cysharp/MagicOnion/releases/tag/2.1.0)
    - [grpc_unity_package.1.22.0-dev.zip](https://packages.grpc.io/archive/2019/06/7fce9da88febd7c0d6f6e0fb90ba31e600624623-9f70381d-a50b-44b5-87cf-62e43d72037e/index.xml)
    - macOS Mojave 10.14.5

# keepalive 関連の gRPC チャンネルのオプション

分かりやすいドキュメントを発見することが出来なかったため、
gRPC Channel に設定可能なコンフィグは [公式のリンク](https://grpc.github.io/grpc/core/group__grpc__arg__keys.html) を参照しました :earth_africa: 

コンフィグを一通り確認したところ keepalive 周りの設定を行うのに、
必要となりそうな主要なオプションは 3つありました :arrow_down:

|名前|説明|
|:------------------:|:------------------:|
|grpc.keepalive_time_ms|ping の間隔 (ms単位)|
|grpc.keepalive_timeout_ms|ping で許容する応答時間 (ms単位)|
|grpc.http2.min_time_between_pings_ms|ping を連続で送信可能な最小間隔 (ms単位)|

`grpc.http2.min_time_between_pings_ms` には `grpc.keepalive_time_ms` と `grpc.keepalive_timeout_ms` を足した値とかを設定しておくと良い気がします :point_up:

# gRPC チャンネルに keepalive 関連のオプションを設定する

クライアント側もサーバ側も同一値を設定したのですが、
念の為、実際に設定した際の [コード](https://gist.github.com/nikaera/adc8f4481987c1225ae8a64bc240e220) について両者記載しておきます :writing_hand: 

## Unity クライアント側

```csharp
// 各種 keepalive 関連のオプションを設定する
List<ChannelOption> options = new List<ChannelOption> {
    new ChannelOption("grpc.keepalive_time_ms", 2000),
    new ChannelOption("grpc.keepalive_timeout_ms", 3000),
    new ChannelOption("grpc.http2.min_time_between_pings_ms", 5000),
};

// gRPC チャネル生成時の第3引数に IEnumerable でオプションを指定する
channel = new Channel(m_MagicOnionHost, 12345, ChannelCredentials.Insecure, options);
```

## MagicOnion サーバ側

```csharp
// 各種 keepalive 関連のオプションを設定する
List<ChannelOption> options = new List<ChannelOption> {
    new ChannelOption("grpc.keepalive_time_ms", 2000),
    new ChannelOption("grpc.keepalive_timeout_ms", 3000),
    new ChannelOption("grpc.http2.min_time_between_pings_ms", 5000),
};

// `UseMagicOnion` 関数の第3引数に IEnumerable でオプションを指定する
await MagicOnionHost.CreateDefaultBuilder()
    .UseMagicOnion(
        new MagicOnionOptions(isReturnExceptionStackTraceInErrorDetail: true),
        new ServerPort("0.0.0.0", 12345, ServerCredentials.Insecure),
        options
    ).RunConsoleAsync();
```

これで MagicOnion に接続しているクライアントの接続が切れたら、
ある程度リアルタイムにネットワークの切断を検知することが可能になります。

具体的にはクライアント側の接続が切れてから MagicOnion サーバ側で :arrow_down: のログ出力が確認できて、
OnDisconnected が呼ばれていることが確認出来ていれば、正常に設定されています :gear:

```terminal
Keepalive watchdog fired. Closing transport.
```

# おわりに

今回は切断検知のための gRPC チャネルの keepalive の設定を行いました :gear: 

gRPC チャネルの keepalive 関連の設定項目は他にも色々あるようなので、
必要に応じてそれら項目を設定することで gRPC チャネルの接続周りの挙動をコントロール出来そうでした。 

また今回の記事をきっかけに各種 [gRPC オプション](https://grpc.github.io/grpc/core/group__grpc__arg__keys.html)を眺めていたのですが、
普通に使ってる分には keepalive 周りの設定しか触ることは無さそうだなと感じました:raised_hand:

# 参考リンク

- [Grpc_arg_keys](https://grpc.github.io/grpc/core/group__grpc__arg__keys.html)
- [node.js の gRPC clientで client side keep-alive (定期的な PING frame 送出)を実現する](https://kiririmode.hatenablog.jp/entry/20190512/1557619277)