Channelzの簡単な紹介
Channelzは、gRPCのさまざまなレベルでの接続に関する包括的な実行時情報を提供するツールです。ネットワーク、パフォーマンス、設定の問題などに悩まされているライブプログラムのデバッグを支援するように設計されています。gRFCには、channelzの設計に関する詳細な説明があり、すべての言語でのchannelz実装の公式リファレンスとなります。このブログの目的は、読者にchannelzサービスを理解してもらい、デバッグにどのように使用できるかを説明することです。この投稿のコンテキストはgRPC-Goに設定されていますが、全体的な考え方は言語を超えて適用可能であるはずです。執筆時点では、channelzはgRPC-GoとgRPC-Javaで利用可能です。C++およびラップ言語のサポートは近日公開予定です。
問題のデバッグに役立つchannelzを使用して、簡単な例を通してchannelzを学びましょう。リポジトリのhelloworld例は、バグのあるシナリオを設定するためにわずかに変更されています。完全なソースコードはここにあります:クライアント、サーバー。
クライアント設定: クライアントは、指定されたターゲットに対して100回のSayHello RPCを呼び出し、ラウンドロビンプローブを使用してワークロードを負荷分散します。各呼び出しには150ミリ秒のタイムアウトがあります。RPCの応答とエラーは、デバッグ目的でログに記録されます。
プログラムを実行すると、ログに図1に示すように、エラーコードDeadlineExceededを伴う断続的なエラーがあることがわかります。
しかし、デッドライン超過エラーの原因については手がかりがなく、多くの可能性があります。
- ネットワークの問題、例えば:接続の喪失
- プロキシの問題、例えば:リクエスト/レスポンスの中間でのドロップ
- サーバーの問題、例えば:リクエストの喪失または応答が遅い


図1。プログラムログのスクリーンショット
より詳細なデバッグ情報のためにgRPCのINFOロギングを有効にし、役立つものが見つかるかどうか確認しましょう。


図2。gRPC INFOログ
図2に示すように、INFOログは、サーバーへの3つの接続すべてが接続されておりRPC送信の準備ができていることを示しています。ログには不審なイベントは表示されません。INFOログから推測できることは、すべての接続が常にアップしているため、接続喪失の仮説は除外できるということです。
根本原因をさらに絞り込むために、channelzの助けを借りましょう。
Channelzは、gRPCサービスを通じてgRPCの内部ネットワーキングコンポーネントの統計情報を提供します。channelzを有効にするには、ユーザーはプログラム内のgRPCサーバーにchannelzサービスを登録し、サーバーを起動するだけです。以下のコードスニペットは、grpc.Serverにchannelzサービスを登録するためのAPIを示しています。これは、例のクライアントですでに実行されていることに注意してください。
import "google.golang.org/grpc/channelz/service"
// s is a *grpc.Server
service.RegisterChannelzServiceToServer(s)
// call s.Serve() to serve channelz service
grpc-zpagesというWebツールが開発されており、channelzデータをWebページを通じて便利に提供します。まず、Webアプリをchannelzサービスを提供しているgRPCポートに接続するように設定します(前のリンクの指示を参照)。次に、ブラウザでchannelz Webページを開きます。図3のようなWebページが表示されるはずです。これでchannelzのクエリを開始できます。


図3。Channelzメインページ
エラーはクライアント側で発生しているため、まずTopChannelsをクリックします。TopChannelsは、親を持たないルートチャネルのコレクションです。gRPC-Goでは、トップチャネルは、ユーザーがNewClientを介して作成し、RPC呼び出しを行うために使用するClientConnです。トップチャネルはchannelzではChannelタイプであり、RPCを発行できる接続の抽象化です。


図4。TopChannelsの結果
TopChannelsをクリックすると、図4のようなページが表示され、関連情報とともにライブのトップチャネル(複数可)がリストされます。
図5に示すように、ID=2のトップチャネルが1つだけあります(角括弧内のテキストは、メモリ内チャネルオブジェクトの参照名であり、言語によって異なる場合があります)。
Dataセクションを見ると、このチャネルで100件中15件の呼び出しが失敗していることがわかります。


図5。トップチャネル(ID = 2)
右側には、チャネルに子Channelsはなく、3つのSubchannels(図6にハイライト表示)と0個のSocketsが表示されています。


図6。チャネル(ID = 2)が所有するサブチャネル
Subchannelは、接続の抽象化であり、負荷分散に使用されます。たとえば、“google.com”にリクエストを送信したいとします。リゾルバは“google.com”を、“google.com”を提供する複数のバックエンドアドレスに解決します。この例では、クライアントはラウンドロビン負荷分散機能で設定されているため、すべてのライブバックエンドに均等なトラフィックが送信されます。次に、各バックエンドへの(論理的)接続がサブチャネルとして表されます。gRPC-Goでは、SubConnはサブチャネルと見なすことができます。
親チャネルが所有する3つのサブチャネルは、RPCを送信するための3つの異なるバックエンドへの3つの接続があることを意味します。それらのそれぞれを詳しく見てみましょう。
そこで、最初にリストされているサブチャネルID(つまり“4[]”)をクリックすると、図7のようなページが表示されます。このサブチャネルですべての呼び出しが成功していることがわかります。したがって、このサブチャネルが問題に関連している可能性は低いです。


図7。サブチャネル(ID = 4)
戻って、サブチャネル5(つまり“5[]”)をクリックします。ここでも、Webページはサブチャネル5も失敗した呼び出しがなかったことを示しています。


図8。サブチャネル(ID = 6)
そして最後に、サブチャネル6をクリックします。今回は、何かが異なります。図8に示すように、このサブチャネルでは34件中15件のRPC呼び出しが失敗しています。そして、親チャネルも15件の失敗した呼び出しがあることを思い出してください。したがって、問題の原因はサブチャネル6です。サブチャネルの状態はREADYであり、接続されておりRPCを送信する準備ができていることを意味します。これにより、ネットワーク接続の問題は除外されます。さらに詳しい情報を得るために、このサブチャネルが所有するSocketを見てみましょう。
Socketは、ファイルディスクリプタにほぼ相当し、一般的に2つのエンドポイント間のTCP接続と見なすことができます。grpc-goでは、http2Clientおよびhttp2ServerがSocketに対応します。ネットワークリスナーもSocketと見なされ、channelzサーバー情報に表示されることに注意してください。


図9。サブチャネル(ID = 6)がSocket(ID = 8)を所有
Socket 8をクリックします。これはページの下部にあります(図9を参照)。そして、図10のようなページが表示されます。
このページは、使用されているセキュリティメカニズム、ストリーム数、メッセージ数、キープアライブ、フロー制御数など、ソケットに関する包括的な情報を提供します。ソケットオプション情報は、多数あり、調査中の問題には関係ないため、スクリーンショットには表示されていません。
Remote Addressフィールドは、問題が発生しているバックエンドが“127.0.0.1:10003”であることを示唆しています。ここのストリーム数は、親サブチャネルの呼び出し数と完全に一致します。これにより、サーバーが積極的にDeadlineExceededエラーを送信していないことがわかります。これは、DeadlineExceededエラーがサーバーから返された場合、すべてのストリームは成功するためです。クライアント側ストリームの成功は、呼び出しが成功したかどうかとは無関係です。HTTP2フレームのEOSビットが設定されているかどうかを受信したかで決定されます(詳細についてはgRFCを参照)。また、送信されたメッセージ数は34であり、呼び出し数に等しく、クライアントがスタックしてデッドライン超過を引き起こした可能性は排除されます。要約すると、問題を127.0.0.1:10003でサービスを提供しているサーバーに絞り込むことができます。サーバーの応答が遅いか、またはその前のプロキシがリクエストをドロップしている可能性があります。


図10。Socket(ID = 8)
ご覧のように、channelzは数回のクリックで問題の潜在的な根本原因を特定するのに役立ちました。これで、特定されたサーバーで何が起こっているかに集中できます。そして再度、channelzはサーバー側でもデバッグを迅速化するのに役立つ可能性があります。
ここでは終了し、読者の皆様に、クライアント側よりも簡単なサーバー側channelzを探求していただくことにします。channelzでは、Serverもチャネルと同様のRPCエントリポイントであり、着信RPCが到着して処理されます。gRPC-Goでは、grpc.Serverはchannelzサーバーに対応します。チャネルとは異なり、サーバーは子としてSocket(リスニングソケットと通常の接続ソケットの両方)のみを持ちます。
読者の皆様へのヒントをいくつかご紹介します。
- アドレス(127.0.0.1:10003)を持つサーバーを探してください。
- 呼び出し数を確認してください。
- サーバーが所有するSocketに移動してください。
- Socketのストリーム数とメッセージ数を確認してください。
サーバーソケットが受信したメッセージ数は、クライアントソケット(Socket 8)が送信したメッセージ数と同じであることがわかるはずです。これにより、誤動作しているプロキシ(リクエストのドロップ)の可能性が排除されます。そして、サーバーソケットが送信したメッセージ数は、クライアント側で受信したメッセージ数と等しく、これはサーバーがデッドライン前に応答を送信できなかったことを意味します。これで、サーバーコードを確認して、これが実際に原因であるかどうかを確認できます。
サーバー設定: サーバー側プログラムは3つのGreeterServerを起動します。そのうち2つは、応答時に遅延をかけない実装(server)を使用し、1つは応答前に100ms〜200msの可変遅延を挿入する実装(slowServer)を使用します。
このデモでわかるように、channelzは問題の原因を迅速に絞り込むのに役立ち、使いやすいです。その他のリソースについては、詳細なchannelzgRFCを参照してください。GitHubで私たちを見つけてください:github.com/grpc/grpc-go。