J'ai un service qui transfère des messages à un taux assez élevé.
Actuellement, il est desservi par akka-tcp et génère 3,5 millions de messages par minute. J'ai décidé d'essayer grpc. Malheureusement, cela a entraîné un débit beaucoup plus faible: ~ 500 000 messages par minute, encore moins.
Pourriez-vous s'il vous plaît recommander comment l'optimiser?
Ma configuration
Matériel : 32 cœurs, tas de 24 Go.
version grpc: 1.25.0
Format de message et point de terminaison
Le message est essentiellement un blob binaire. Le client diffuse de 100 000 à 1 million de messages et plus dans la même demande (de manière asynchrone), le serveur ne répond avec rien, le client utilise un observateur sans opération
service MyService {
rpc send (stream MyMessage) returns (stream DummyResponse);
}
message MyMessage {
int64 someField = 1;
bytes payload = 2; //not huge
}
message DummyResponse {
}
Problèmes: le taux de messages est faible par rapport à la mise en œuvre d'Akka. J'observe une faible utilisation du processeur, donc je soupçonne que l'appel grpc se bloque réellement en interne malgré le contraire. L'appel en onNext()
effet ne revient pas immédiatement mais il y a aussi du GC sur la table.
J'ai essayé de générer plus d'expéditeurs pour atténuer ce problème, mais je n'ai pas obtenu beaucoup d'amélioration.
Mes résultats Grpc alloue en fait un tampon de 8 Ko sur chaque message lors de sa sérialisation. Voir le stacktrace:
java.lang.Thread.State: BLOCKED (on object monitor) sur com.google.common.io.ByteStreams.createBuffer (ByteStreams.java:58) sur com.google.common.io.ByteStreams.copy (ByteStreams.java: 105) sur io.grpc.internal.MessageFramer.writeToOutputStream (MessageFramer.java:274) sur io.grpc.internal.MessageFramer.writeKnownLengthUncompressed (MessageFramer.java:230) sur io.grpc.internal.MessageFramer.compresseur : 168) sur io.grpc.internal.MessageFramer.writePayload (MessageFramer.java:141) sur io.grpc.internal.AbstractStream.writeMessage (AbstractStream.java:53) sur io.grpc.internal.ForwardingClientStream.writeMessage (Forwarding). java: 37) sur io.grpc.internal.DelayedStream.writeMessage (DelayedStream.java:252) sur io.grpc.internal.ClientCallImpl.sendMessageInternal (ClientCallImpl.java:473) à io.grpc.internal.ClientCallImpl.sendMessage (ClientCallImpl.java:457) à io.grpc.ForwardingClientCall.sendMessage (ForwardingClientCall.java:Client.For.Cost.For.Client.Force.For.ClientCompendio.ForientiFormage.Forward.ClientCompendors.Forward.ClientCall.js (ForwardingClientCall.java:37) sur io.grpc.stub.ClientCalls $ CallToStreamObserverAdapter.onNext (ClientCalls.java:346)
Toute aide avec les meilleures pratiques sur la construction de clients grpc à haut débit appréciée.
scalapb
. Cette trace de pile provenait probablement du code généré par scalapb. J'ai supprimé tout ce qui concerne le scalapb, mais cela n'a pas beaucoup amélioré les performances.Réponses:
J'ai résolu le problème en créant plusieurs
ManagedChannel
instances par destination. Malgré les articles, unManagedChannel
peut générer lui-même suffisamment de connexions, donc une instance suffit, ce n'était pas vrai dans mon cas.Les performances sont en parité avec l'implémentation akka-tcp.
la source
Question interessante. Les packages de réseaux informatiques sont codés à l'aide d'une pile de protocoles , et ces protocoles sont construits en plus des spécifications du précédent. Par conséquent, les performances (débit) d'un protocole sont limitées par les performances de celui utilisé pour le construire, car vous ajoutez des étapes d'encodage / décodage supplémentaires par-dessus celle sous-jacente.
Par exemple,
gRPC
est construit au-dessus deHTTP 1.1/2
, qui est un protocole sur la couche Application , ouL7
, en tant que tel, ses performances sont liées par les performances deHTTP
. Maintenant,HTTP
lui-même est construit au-dessus deTCP
, qui se trouve dans la couche Transport , ouL4
, nous pouvons donc en déduire que legRPC
débit ne peut pas être supérieur à un code équivalent servi dans laTCP
couche.En d'autres termes: si votre serveur est capable de gérer des
TCP
packages bruts , comment l'ajout de nouvelles couches de complexité (gRPC
) améliorerait-il les performances?la source
gRPC
vous payez également une fois pour établir une connexion, mais vous avez ajouté le fardeau supplémentaire de l'analyse de protobuf. Quoi qu'il en soit, il est difficile de faire des suppositions sans trop d'informations, mais je parierais qu'en général, puisque vous ajoutez des étapes d'encodage / décodage supplémentaires dans votre pipeline, l'gRPC
implémentation serait plus lente que la socket Web équivalente.Je suis assez impressionné par la qualité des performances d'Akka TCP ici: D
Notre expérience a été légèrement différente. Nous travaillions sur des instances beaucoup plus petites utilisant Akka Cluster. Pour Akka distant, nous sommes passés d'Akka TCP à UDP en utilisant Artery et avons obtenu un taux beaucoup plus élevé + un temps de réponse plus faible et plus stable. Il y a même une configuration dans Artery qui aide à équilibrer la consommation du processeur et le temps de réponse après un démarrage à froid.
Ma suggestion est d'utiliser un cadre basé sur UDP qui prend également en charge la fiabilité de la transmission pour vous (par exemple, Artery UDP), et de sérialiser simplement en utilisant Protobuf, au lieu d'utiliser gRPC à chair pleine. Le canal de transmission HTTP / 2 n'est pas vraiment à des fins de temps de réponse bas débit élevé.
la source