ベストプラクティス

ベストプラクティス

全般

  • コールバックAPIを使用してください
  • コメント付きのヘッダーファイルを探してください third_party/grpc/include/grpcpp
  • 常にRPCにデッドラインを設定してください。 ブログ記事で説明しています。長時間続くストリーミングRPCでは設定が難しいですが、アプリケーションでカスタムロジックを実装してメッセージにデッドラインを追加できます。

ストリーミングRPC

  • すべての送信メッセージが必要な場合は、失敗するまですべてのメッセージを読み取ってください。コールバックAPIの場合はok=falseで反応が呼び出されるまで、非同期APIの場合はタグにok=falseがあるまで、同期APIの場合はReadが失敗するまで読み取ります。これはメッセージ数を数えるよりも信頼性があります。
  • 一度に進行できる読み取りと書き込みは1つだけです。これはベストプラクティスというよりAPIの要件ですが、再度言及する価値があります。
  • アプリケーションが双方向のデータストリームを持っている場合は、クライアント-サーバーとサーバー-クライアントモデルではなく、双方向ストリーミングを使用してください。これにより、一貫した負荷分散が可能になり、gRPCでより適切にサポートされます。

コールバックAPI固有

このセクションでは、「操作」はStartReadStartWrite(およびそのバリアント)、SendInitialMetadataとして定義されます。Finishも操作ですが、議論に関連しない場合が多いです。

「反応」は、リアクター内のオーバーライド可能なコールバック、例えばOnReadDoneOnWriteDoneOnCancelOnInitialMetadataDoneとして定義されます。OnDoneも反応ですが、最終的な反応として、ここでの指示は関連しない場合があります。

ベストプラクティス

  • 反応は高速であるべきです。ブロッキングまたは長時間実行/重いタスクを実行したり、スリープしたりしないでください。プロセス内の他のRPCに影響を与える可能性があります。

ストリーミング

  • 反応外で操作を開始する場合は、Holdを使用してください。反応外からクライアントで操作を開始する場合、holdsを使用する必要がある場合があります。これにより、保留が解除されるまでOnDone()の実行が防止され、ストリームにエラーがある場合の未完了の操作との最終クリーンアップの競合を防ぐことができます。反応のbool ok値は、ストリームが終了したかどうかを反映し、これ以降に開始されたすべての操作はok=falseになります。

  • 反応を同期してください。反応は並列で実行される可能性があります。たとえば、OnReadDoneOnWriteDoneと同時に実行される可能性があります。適切に同期してください。

  • Falseまで読み取るメッセージ数などを数えるのではなく、OnReadDone(ok=false)まで読み取ります。

    サーバー側では、これはクライアントが書き込み完了を呼び出す必要があることに注意してください。これは推奨されます。サーバー側は特別なことをする必要はありません。Finishはストリームの終了をシグナルします。

    アプリケーションがFinish呼び出しを通じて送信したステータスは、すべての受信メッセージが消費されるまでクライアントに表示されない場合があります。

よくある質問

全般

  1. gRPCの問題をデバッグするにはどうすればよいですか?

    トラブルシューティングを参照してください。

コールバックストリーミングAPI

  1. クライアントのハーフクローズは必要または期待されていますか?

    クライアントのハーフクローズは強く推奨されます。これにより、サーバーはOnReadDone(ok=false)まで読み取りを続けることができ、これはサーバーとクライアントの両方で推奨されます。ただし、必須ではありません。サーバーは、クライアントデータのすべてを消費する前にFinish()を選択することもできます。

  2. クライアント側で操作をキャンセルするにはどうすればよいですか?

    ClientContext::TryCancel()

  3. OnDoneが呼び出される前に、クライアントはワイヤ上のデータを読み取る必要がありますか?

    ベストプラクティスは、常にクライアント側でok=falseまで読み取ることです。

    クライアントは、サーバーFinishからOKステータスを受け取る前に、すべての受信データを読み取る必要があります。ただし、キャンセル、デッドライン、またはストリームの中断によるエラーなどのエラー状態はいつでも到着する可能性があります。

    エラー状態を指定した明示的なサーバーFinishがサーバー書き込みの後ろにキューイングされるか、すぐに配信されるかについての保証はありません。したがって、ステータスの配信を保証するために、クライアントは常にok=falseまで読み取って、すべての受信メッセージを消費する必要があります。

    サーバーがエラー状態を指定してFinishを呼び出した場合、メッセージの配信が保証されないため、エラー状態は成功を伝えたり、クライアントへの追加の指示に使用したりしないでください。代わりに、末尾メタデータを使用してください。

  4. クライアントでOnDoneが呼び出されるのはいつですか?

    クライアントにステータスが利用可能になったとき(すべての受信データが読み取られ、サーバーがFinishを呼び出したか、すぐに配信されるエラーである)、すべてのユーザー反応の実行が完了し、すべての保留が解除されたときに呼び出されます。

  5. サーバーでOnDoneが呼び出されるのはいつですか?

    すべての反応が完了し(該当する場合はOnCancelを含む)、サーバーがFinishを呼び出した後に呼び出されます。

  6. 「リアクター内フロー」と「リアクター外フロー」とは何ですか、そしてなぜ重要ですか?

    リアクター内フローとは、反応内(またはリアクターコンストラクタ)から操作が開始される場合です。たとえば、OnWriteDoneが別の書き込みを開始する場合などです。これは、一度に1つの読み取りと1つの書き込みしか実行できないため理にかなっており、反応から開始することでそれを維持するのに役立ちます。

    リアクター外フローとは、ストリーム上の操作が反応外から開始される場合です。これは、反応はブロックするべきではなく、アプリケーションが次の読み取りまたは書き込みを実行する準備ができていない可能性があるため理にかなっています。リアクター外フローでは、クライアント側で同期するためにHoldを使用する必要があることに注意してください。サーバー側はFinishを使用してリアクター外フローの呼び出しを同期します。アプリケーションはFinishを呼び出した後にそれ以上の操作を開始すべきではありません。

  7. Holdとは何ですか、どのように、いつ使用しますか?

    OnDoneがいつ呼び出されるかを同期するために使用され、リアクター外フローが使用されている場合にのみ必要です。Holdはクライアント側にのみ存在することに注意してください。

  8. サーバーがFinishを呼び出したのに、クライアントがStartWriteなどで新しい操作を開始し続けた場合はどうなりますか?

    OnWriteDone(ok=false)は、OnDoneが呼び出されるまで、書き込みが開始されるたびに呼び出されます。

  9. リアクターはいつ削除できますか?

    リアクターはOnDoneで削除できます。OnDoneからリアクターの基底クラスのメソッドを呼び出すことはできません。また、OnDoneが呼び出された後、gRPCはリアクターオブジェクトにアクセスしません。OnDoneが実行されている間に操作が開始されないことを保証するのは、アプリケーションの責任です。

  10. 反応は同時に実行できますか?たとえば、OnReadInitialMetadataDoneOnReadDoneと同時に実行できますか?

    はい、ほとんどの反応は並列で実行できます。OnDoneのみが最終操作として単独で実行されます。

  11. メタデータが空の場合でも、OnReadInitialMetadataDoneは常に呼び出されますか?

    はい、これはクライアントにメタデータが空であることを通知するために使用されます。すべての反応と同様に、ユーザーアプリケーションは、この反応をオーバーライドする必要はありません。

  12. 初期メタデータが明示的なSendInitialMetadataではなく、最初の書き込みとともに送信された場合、サーバーでOnSendInitialMetadataDoneは呼び出されますか?

    いいえ、明示的に要求する必要があります。暗黙的な呼び出しにはコールバックは返されません。

  13. クライアントがWriteLastを呼び出した場合、OnWriteDoneOnWritesDoneDoneの両方のコールバックが呼び出されますか?Write(options.set_last_message = true)を呼び出した場合はどうなりますか?

    ペイロードがある場合、OnWriteDone()のみが呼び出されます。OnWritesDoneDoneWritesDone()に応答してのみ呼び出されます。

  14. 書き込みが保留中(つまり、OnWriteDone()がまだ呼び出されていない)の間にWritesDone()を呼び出すことはできますか?

    はい、トランスポートがこれらを順序付けします。

  15. クライアントでOnReadInitialMetadataDoneok=falseとともに呼び出されるのはいつですか?

    これはエラーがある場合にのみ呼び出されます。

  16. ユーザーは、中間のOnSendInitialMetadataDoneを待たずに、SendInitialMetadataStartWriteを呼び出すことはできますか?

    これはStartWriteWritesDoneに似ています。トランスポートが順序付けする場合、順序付けを強制する必要はありませんが、ユーザーは任意の順序でコールバックが呼び出される可能性があります。

  17. サーバーのOnCancelはいつ実行されますか?

    これはストリーミングに固有ではないことに注意してください。帯域外キャンセル時に呼び出されます。つまり、ストリームにエラーがある場合(接続中止など)、クライアントまたはサーバー側でキャンセルが要求された場合、またはデッドラインが期限切れになった場合です。クライアントがもはやデータを処理していないことのシグナルとして使用できます。

    他の反応と並行して実行される可能性があります。OnCancelの後に呼び出された操作、またはOnCancel内で開始された操作は、ok=falseで反応が呼び出されます。

    サーバーがエラー状態を指定してFinishを呼び出した場合、OnCancelはもはや実行されないことに注意してください。これは帯域外キャンセルとは見なされないためです。

    順序によっては、Finishが呼び出された場合、OnCancelが実行される場合とされない場合があります。ただし、OnDoneは常に最終コールバックです。

  18. OnCancelが実行された場合でも、サーバーはFinishを呼び出す必要がありますか?

    はい、Finishに渡されるステータスは無視されますが。

  19. OnCancelが呼び出された場合でも、OnDoneは呼び出されますか?

    はい、OnDoneは最終コールバックであり、サーバーがFinishを呼び出し、他のすべての反応が完了したら呼び出されます。

  20. ユーザーはOnCancelの後にさらに操作(Start*)を呼び出すことができますか?

    はい、ただし、それらはすべてok=falseで反応が実行されます。サーバーFinishを呼び出した後にそれらを呼び出すことは無効です。

  21. contextIsCancelled()がtrueを返すのはいつですか?

    これは、ストリームにエラーがある場合、クライアントまたはサーバー側でキャンセルが要求された場合、またはデッドラインが期限切れになった場合に設定されます。b/138186533が解決されると、これらの種類のいずれかのエラーが反応がok=falseで呼び出された理由である場合、これらの反応が呼び出される前に設定されます。

  22. 操作ごと(例:StartRead)のデッドラインはありますか、それともRPCごとですか?

    RPCごとのみです。

  23. ユーザーがstub->MyBidiStreamRPC(); context->TryCancel()を行った場合、ユーザーはStartCallを呼び出す必要がありますか?

    はい、stub->MyBidiStreamRPC()が呼び出されたら必要です。

  24. サーバーがReadまたはWriteの保留中にFinishを呼び出すことは合法ですか?

    読み取りの場合は問題ありません。OnReadDone(ok=false)が呼び出されます。書き込みが保留中のFinishを呼び出すことは、APIの不正使用です。Finishはデータなしの最終書き込みと見なされる可能性があり、これは1つの書き込み中のルールに違反するためです。

  25. サーバーがReadを保留中にFinishを呼び出した場合はどうなりますか?

    OnReadDone(ok=false)が呼び出されます。

  26. クライアントリアクターのOnDoneで別の操作(例:読み取りまたは書き込み)を開始できますか?

    いいえ、それはAPIの合法的な使用ではありません。

  27. サーバーでFinishを呼び出した後に操作を開始できますか?

    これは良いプラクティスではありません。ただし、反応内で新しい操作を開始した場合、それに対応する反応はok=falseで呼び出されます。反応外フローで開始することは、操作がOnDoneと競合する可能性があるため、不正で問題があります。

最終更新日 2024年6月25日: C++ ベストプラクティス (#1309) (4709643)