かなり高速でメッセージを転送するサービスがあります。
現在、それはakka-tcpによって提供されており、毎分350万のメッセージを作成します。 grpcを試してみることにしました。残念ながら、その結果、スループットははるかに小さくなりました。1分あたり約50万メッセージがさらに少なくなります。
それを最適化する方法をお勧めしていただけませんか?
マイセットアップ
ハードウェア:32コア、24 Gbヒープ。
grpcバージョン:1.25.0
メッセージ形式とエンドポイント
メッセージは基本的にバイナリBLOBです。クライアントは100K-1M以上のメッセージを同じリクエストに(非同期で)ストリーミングし、サーバーは何も応答せず、クライアントは何もしないオブザーバーを使用します
_service MyService {
rpc send (stream MyMessage) returns (stream DummyResponse);
}
message MyMessage {
int64 someField = 1;
bytes payload = 2; //not huge
}
message DummyResponse {
}
_
問題:メッセージレートがakka実装と比較して低い。 CPU使用率が低いので、別の言い方をしてもgrpc呼び出しが内部で実際にブロックしているのではないかと思います。実際、onNext()
を呼び出してもすぐには戻りませんが、テーブルにGCもあります。
この問題を軽減するために、より多くの送信者を生成しようとしましたが、あまり改善されませんでした。
私の所見 Grpcは、メッセージをシリアル化するときに、実際に各メッセージに8KBバイトのバッファーを割り当てます。スタックトレースを見てください:
Java.lang.Thread.State:BLOCKED(オブジェクトモニター上)com.google.common.io.ByteStreams.createBuffer(ByteStreams.Java:58)at com.google.common.io.ByteStreams.copy(ByteStreams.Java: 105)io.grpc.internal.MessageFramer.writeUncompressed(MessageFramer.Javaでio.grpc.internal.MessageFramer.writeKnownLengthUncompressed(MessageFramer.Java:230)でio.grpc.internal.MessageFramer.writeToOutputStream(MessageFramer.Java:274)に:168)io.grpc.internal.MessageFramer.writePayload(MessageFramer.Java:141)at io.grpc.internal.AbstractStream.writeMessage(AbstractStream.Java:53)at io.grpc.internal.ForwardingClientStream.writeMessage(ForwardingClientStream。 Java:37)io.grpc.internal.DelayedStream.writeMessage(DelayedStream.Java:252)at io.grpc.internal.ClientCallImpl.sendMessageInternal(ClientCallImpl.Java:473)at io.grpc.internal.ClientCallImpl.sendMessage(ClientCallImpl .Java:457)io.grpc.ForwardingClientCall.sendMessage(ForwardingClientCall.Java:37)at io.grpc.ForwardingClientCall.sendMessage(Fo rwardingClientCall.Java:37)io.grpc.stub.ClientCalls $ CallToStreamObserverAdapter.onNext(ClientCalls.Java:346)で
高スループットのgrpcクライアントを構築するためのベストプラクティスに関する支援があれば幸いです。
宛先ごとにいくつかのManagedChannel
インスタンスを作成することで問題を解決しました。記事にもかかわらず、ManagedChannel
はそれ自体で十分な接続を生成できるため、1つのインスタンスで十分であると述べていますが、私の場合はそうではありませんでした。
パフォーマンスはakka-tcp実装と同等です。
興味深い質問です。コンピュータネットワークパッケージは プロトコルのスタック を使用してエンコードされ、そのようなプロトコルは以前のプロトコルの仕様の上に構築されています。したがって、基礎となるプロトコルの上に追加のエンコーディング/デコーディングステップを追加しているため、プロトコルのパフォーマンス(スループット)は、プロトコルの構築に使用されたもののパフォーマンスによって制限されます。
たとえば、gRPC
はHTTP 1.1/2
の上に構築されます。これは、アプリケーションレイヤーのプロトコル、またはL7
などです。そのパフォーマンスはHTTP
のパフォーマンスによって制限されます。これでHTTP
自体がTCP
の上に構築されます。これはTransportレイヤーまたはL4
にあるので、それを推定できますgRPC
スループットは、TCP
レイヤーで提供される同等のコードより大きくすることはできません。
言い換えると、サーバーが未加工のTCP
パッケージを処理できる場合、新しい複雑なレイヤー(gRPC
)を追加するとどのようにパフォーマンスが向上しますか?
私はAkka TCPがここでどれほど優れているかについて非常に感銘を受けました:D
私たちの経験は少し異なりました。 Akka Clusterを使用して、はるかに小さなインスタンスで作業していました。 Akkaリモーティングでは、Arteryを使用してAkka TCP=からUDPに変更し、はるかに高いレート+より低く、より安定した応答時間を実現しました。Arteryには、CPU消費とコールドスタートからの応答時間。
私の提案は、送信の信頼性も処理するUDPベースのフレームワーク(Artery UDPなど)を使用し、フルフレッシュgRPCを使用する代わりに、Protobufを使用してシリアル化することです。 HTTP/2伝送チャネルは、実際には高スループット、低応答時間を目的としたものではありません。