Ich habe einen Dienst, der Nachrichten mit einer ziemlich hohen Rate überträgt.
Derzeit wird es von akka-tcp bereitgestellt und sendet 3,5 Millionen Nachrichten pro Minute. Ich beschloss, grpc auszuprobieren. Leider führte dies zu einem viel geringeren Durchsatz: ~ 500.000 Nachrichten pro Minute und noch weniger.
Könnten Sie bitte empfehlen, wie Sie es optimieren können?
Mein Setup
Hardware : 32 Kerne, 24 GB Heap.
grpc version : 1.25.0
Nachrichtenformat und Endpunkt
Nachricht ist im Grunde ein binärer Blob. Der Client überträgt 100K - 1M und mehr Nachrichten in dieselbe Anforderung (asynchron), der Server antwortet mit nichts, der Client verwendet einen No-Op-Beobachter
service MyService {
rpc send (stream MyMessage) returns (stream DummyResponse);
}
message MyMessage {
int64 someField = 1;
bytes payload = 2; //not huge
}
message DummyResponse {
}
Probleme: Die Nachrichtenrate ist im Vergleich zur akka-Implementierung niedrig. Ich beobachte eine geringe CPU-Auslastung, daher vermute ich, dass der grpc-Aufruf tatsächlich intern blockiert, obwohl etwas anderes angegeben ist. BerufungonNext()
kehrt zwar nicht sofort zurück, aber es liegt auch GC auf dem Tisch.
Ich habe versucht, mehr Absender zu erzeugen, um dieses Problem zu beheben, aber keine großen Verbesserungen erzielt.
Meine Ergebnisse Grpc weist jeder Nachricht bei der Serialisierung tatsächlich einen 8-KB-Byte-Puffer zu. Siehe die Stapelverfolgung:
java.lang.Thread.State: BLOCKED (auf dem Objektmonitor) unter com.google.common.io.ByteStreams.createBuffer (ByteStreams.java:58) unter com.google.common.io.ByteStreams.copy (ByteStreams.java: 105) unter io.grpc.internal.MessageFramer.writeToOutputStream (MessageFramer.java:274) unter io.grpc.internal.MessageFramer.writeKnownLengthUncompressed (MessageFramer.java:230) unter io.grpc : 168) unter io.grpc.internal.MessageFramer.writePayload (MessageFramer.java:141) unter io.grpc.internal.AbstractStream.writeMessage (AbstractStream.java:53) unter io.grpc.internal.ForwardingClientStream.writeM. java: 37) bei io.grpc.internal.DelayedStream.writeMessage (DelayedStream.java:252) bei io.grpc.internal.ClientCallImpl.sendMessageInternal (ClientCallImpl.java:473) unter io.grpc.internal.ClientCallImpl.sendMessage (ClientCallImpl.java:457) unter io.grpc.ForwardingClientCall.sendMessage (ForwardingClientCall.allCall.sendMallage) (ForwardingClientCall.java:37) unter io.grpc.stub.ClientCalls $ CallToStreamObserverAdapter.onNext (ClientCalls.java:346)
Jede Hilfe mit Best Practices beim Aufbau von Grpc-Clients mit hohem Durchsatz wird geschätzt.
scalapb
. Wahrscheinlich war dieser Stacktrace tatsächlich von Scalapb-generiertem Code. Ich habe alles entfernt, was mit Scalapb zu tun hat, aber es hat nicht viel zur Leistung beigetragen.Antworten:
Ich habe das Problem gelöst, indem ich mehrere
ManagedChannel
Instanzen pro Ziel erstellt habe. Obwohl Artikel besagen, dass eineManagedChannel
Dose selbst genügend Verbindungen erzeugen kann, reicht eine Instanz aus, was in meinem Fall nicht der Fall war.Die Leistung entspricht der Implementierung von akka-tcp.
quelle
Interessante Frage. Computernetzwerkpakete werden unter Verwendung eines Protokollstapels codiert , und solche Protokolle bauen auf den Spezifikationen des vorherigen auf. Daher ist die Leistung (Durchsatz) eines Protokolls an die Leistung des Protokolls gebunden, mit dem es erstellt wurde, da Sie zusätzlich zum zugrunde liegenden Protokoll zusätzliche Codierungs- / Decodierungsschritte hinzufügen.
Zum Beispiel
gRPC
wird darauf aufgebautHTTP 1.1/2
, was ein Protokoll auf der Anwendungsschicht ist , oderL7
, und als solches ist seine Leistung an die Leistung von gebundenHTTP
. Jetzt wirdHTTP
selbst darauf aufgebautTCP
, was sich auf der Transportebene befindet , oderL4
wir können daraus schließen, dass dergRPC
Durchsatz nicht größer sein kann als ein äquivalenter Code, der in derTCP
Ebene bereitgestellt wird.Mit anderen Worten: Wenn Ihr Server in der Lage ist, Rohpakete zu verarbeiten
TCP
, wie würde das Hinzufügen neuer Komplexitätsebenen (gRPC
) die Leistung verbessern?quelle
gRPC
Sie zahlen auch einmal eine Verbindung für den Aufbau, aber Sie haben die zusätzliche Belastung der Parsen protobuf hinzugefügt. Wie auch immer, es ist schwierig, ohne zu viele Informationen zu raten, aber ich würde wetten, dass diegRPC
Implementierung im Allgemeinen langsamer ist als die entsprechende Web-Socket-Implementierung , da Sie zusätzliche Codierungs- / Decodierungsschritte in Ihre Pipeline aufnehmen .Ich bin ziemlich beeindruckt davon, wie gut Akka TCP hier abgeschnitten hat: D.
Unsere Erfahrung war etwas anders. Wir haben mit Akka Cluster an viel kleineren Instanzen gearbeitet. Für das Akka-Remoting haben wir mithilfe von Artery von Akka TCP zu UDP gewechselt und eine viel höhere Rate + niedrigere und stabilere Reaktionszeit erzielt. Es gibt sogar eine Konfiguration in Artery, die hilft, den CPU-Verbrauch und die Reaktionszeit nach einem Kaltstart auszugleichen.
Mein Vorschlag ist, ein UDP-basiertes Framework zu verwenden, das auch die Übertragungszuverlässigkeit für Sie übernimmt (z. B. das Artery UDP), und nur mit Protobuf zu serialisieren, anstatt Vollfleisch-gRPC zu verwenden. Der HTTP / 2-Übertragungskanal ist nicht wirklich für Zwecke mit hohem Durchsatz und geringer Antwortzeit geeignet.
quelle