gRPC Keepalive について
一般的に Keepalive と呼ばれているものには二種類があります。
- TCP Keepalive のようなもの
- 定期的に ping を飛ばして死活監視をする
- HTTP Keepalive のようなもの
- 通信が終わっても(一定時間は)接続を閉じずに次の通信を待つ
gRPC Keepalive と呼ばれているものは TCP Keepalive に相当するような機能です。もちろん、TCP Keepalive はトランスポート層で動作する一方、gRPC Keepalive はアプリケーション層で動作するなどの違いはあります。
gRPC Keepalive の主なユースケースは、ストリーミング通信を行っている最中に実際のデータのやり取りがない状態がしばらく続いた場合、相手がまだ生きているかどうかを確かめることです。
gRPC Keepalive に関連するプロポーザル:
- https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md
- https://github.com/grpc/proposal/blob/master/A9-server-side-conn-mgt.md
一方で、gRPC ではデフォルトの状態で HTTP Keepalive のようなものも実現されています。クライアントからチャンネルを閉じなければ、サーバ側から一方的に接続を閉じるようにはなっていません。この挙動は後述の「接続を閉じる条件の設定」によって変更可能です。
gRPC のサーバ/クライアントでの設定項目
もし gRPC Keepalive を使用する場合は、サーバの設定でクライアントからの ping 受信を許容するようにしなければなりません。設定された頻度を超えて過剰な ping が送信されると、サーバは GOAWAY フレームを送信して切断することになります。多数のクライアントから想定を大きく超える数の ping がサーバになだれ込んでしまうと実質的な DDoS 状態に陥ってしまうため、このような仕様になっています。
下記のサンプルで実際に gRPC Keepalive を設定しているので、Python の場合のオプションの設定方法はこれを参考にしてください。
gRPC Keepalive 自体の設定
-
grpc.keepalive_time_ms
- keepalive ping が送信されるまでの期間
- サーバ側デフォルト: 2時間
- クライアント側デフォルト: 無限(無効)
-
grpc.keepalive_timeout_ms
- keepalive ping を送信してから応答を待つ時間
タイムアウトすると接続を閉じる - サーバ側デフォルト: 20秒
- クライアント側デフォルト: 20秒
- keepalive ping を送信してから応答を待つ時間
-
grpc.keepalive_permit_without_calls
- 実行中の RPC がない状態でも ping の送信を許可 (0 : false; 1 : true)
- サーバ側デフォルト: 0
- クライアント側デフォルト: 0
gRPC Keepalive に関連する設定
-
grpc.http2.min_ping_interval_without_data_ms
- リクエストを送信せずに連続して ping の受信を受け付ける最小時間間隔
- サーバ側デフォルト: 5分
-
grpc.http2.max_pings_without_data
- クライアントがリクエストを送信する前に送信できる ping の数(0 で無制限)
- サーバ側デフォルト: 2
- クライアント側デフォルト: 2
接続を閉じる条件の設定
-
grpc.max_connection_idle_ms
- チャネルに未処理の通信がない状態の最大時間
これが経過するとサーバは接続を閉じる - サーバ側デフォルト: 無限
- チャネルに未処理の通信がない状態の最大時間
-
grpc.max_connection_age_ms
- 通信の有無にかかわらずチャンネルが存在できる最大時間(寿命)
ロードバランサがサーバのスケーリングのために定期的に再接続させたい場合に利用される - サーバ側デフォルト: 無限
- 通信の有無にかかわらずチャンネルが存在できる最大時間(寿命)
-
grpc.max_connection_age_grace_ms
- チャンネルが寿命に達した時点で通信が行われていた場合、その通信が終わるのを待つ猶予時間
- サーバ側デフォルト: 無限