Dans cet épisode de ma série d' articles gRPC et ASP.NET Core, nous verrons comment connecter la fonctionnalité de compression de réponse des services gRPC.
REMARQUE : dans cet article, je couvre certains des dĂ©tails de compression que j'ai appris en apprenant les paramĂštres et les mĂ©thodes d'appel. Il y aura probablement des approches plus prĂ©cises et plus efficaces pour obtenir les mĂȘmes rĂ©sultats.
Cet article fait partie d'une série sur gRPC et ASP.NET Core .
Quand devez-vous activer la compression dans GRPC?
Réponse courte: cela dépend de vos charges utiles.
RĂ©ponse longue:
gRPC utilise un tampon de protocole comme outil de sérialisation des messages de demande et de réponse envoyés sur le réseau. Le tampon de protocole crée un format de sérialisation binaire conçu par défaut pour les charges utiles petites et efficaces. Par rapport aux charges utiles JSON classiques, protobuf fournit une taille de message plus modeste. JSON est assez verbeux et lisible. Par conséquent, il inclut les noms de propriété dans les données envoyées sur le réseau, ce qui augmente le nombre d'octets à transférer.
Le tampon de protocole utilise des entiers comme identificateurs pour les données transmises sur le réseau. Il utilise le concept de variantes de base 128, qui permet aux champs avec des valeurs de 0 à 127 de ne nécessiter qu'un seul octet pour le transport. Dans de nombreux cas, il est possible de limiter vos messages aux champs de cette plage. Les grands entiers nécessitent plus d'un octet.
Donc, rappelez-vous, la charge utile protobuf est dĂ©jĂ assez petite, car le format vise Ă rĂ©duire les octets envoyĂ©s sur le rĂ©seau Ă la plus petite taille possible. Cependant, il existe encore un potentiel de compression sans perte supplĂ©mentaire en utilisant un format tel que GZip. Ce potentiel doit ĂȘtre testĂ© sur vos charges utiles, car vous ne verrez une rĂ©duction de taille que si votre charge utile contient suffisamment de donnĂ©es textuelles rĂ©pĂ©titives pour bĂ©nĂ©ficier de la compression. Peut-ĂȘtre que pour les petits messages de rĂ©ponse, essayer de les compresser peut entraĂźner plus d'octets que d'utiliser un message non compressĂ©; ce qui n'est clairement pas bon.
Il convient également de noter la surcharge de compression du processeur, qui peut l'emporter sur le gain obtenu grùce à la réduction de taille. Vous devez suivre la surcharge du processeur et de la mémoire pour les demandes aprÚs avoir modifié le niveau de compression pour obtenir une image complÚte de vos services.
L'intĂ©gration ASP.NET Core Server n'utilise pas la compression par dĂ©faut, mais nous pouvons l'activer pour l'ensemble du serveur ou des services spĂ©cifiques. Cela semble ĂȘtre une valeur par dĂ©faut raisonnable car vous pouvez suivre vos rĂ©ponses pour diffĂ©rentes mĂ©thodes au fil du temps et Ă©valuer les avantages de les compresser.
Comment activer la compression de réponse dans GRPC?
Jusqu'à présent, j'ai trouvé deux approches principales pour connecter la compression de réponse gRPC. Vous pouvez le configurer au niveau du serveur afin que tous les services gRPC appliquent la compression aux réponses ou au niveau du service individuel.
Configuration au niveau du serveur
services.AddGrpc(o =>
{
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
Startup.cs GitHub
Lors de l'enregistrement d'un service gRPC dans un conteneur d'injection de dépendances à l'aide d'une méthode à l'
AddGrpc
intérieur ConfigureServices
, nous avons la possibilité de configurer dans GrpcServiceOptions
. à ce niveau, les paramÚtres affectent tous les services gRPC que le serveur implémente.
En utilisant une surcharge de méthode d'extension
AddGrpc
, nous pouvons fournir Action<
GrpcServiceOptions>
. Dans l'extrait de code ci-dessus, nous avons choisi l'algorithme de compression «gzip». Nous pouvons également établir CompressionLevel
en manipulant le temps que nous sacrifions pour la compression des données pour obtenir une taille plus petite. Si le paramÚtre n'est pas spécifié, l'implémentation actuelle utilise par défaut CompressionLevel.Fastest
. Dans l'extrait de code précédent, nous avons accordé plus de temps à la compression pour réduire le nombre d'octets à la plus petite taille possible.
Configuration du niveau de service
services.AddGrpc()
.AddServiceOptions<WeatherService>(o =>
{
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
Startup.cs GitHub
L'appel
AddGrpc
revient IGrpcServerBuilder
. Nous pouvons appeler une méthode d'extension appelée sur le constructeur AddServiceOptions
pour fournir des paramÚtres pour chaque service séparément. Cette méthode est générique et prend le type de service gRPC auquel les paramÚtres doivent s'appliquer.
Dans l'exemple précédent, nous avons choisi de fournir des paramÚtres pour les appels gérés par l'implémentation
WeatherService
. Ă ce niveau, les mĂȘmes options sont disponibles que celles dont nous avons discutĂ© pour la configuration au niveau du serveur. Dans ce scĂ©nario, les autres services gRPC sur ce serveur ne recevront pas les options de compression que nous avons dĂ©finies pour ce service particulier.
Demandes du client GRPC
Maintenant que la compression des rĂ©ponses est activĂ©e, nous devons nous assurer que nos requĂȘtes indiquent que notre client accepte le contenu compressĂ©. En fait, cela est activĂ© par dĂ©faut lorsqu'il est utilisĂ©
GrpcChannel
avec une méthode créée ForAddress
, nous n'avons donc rien Ă faire dans notre code client.
var channel = GrpcChannel.ForAddress("https://localhost:5005");
Program.cs GitHub
Les canaux crĂ©Ă©s de cette maniĂšre envoient dĂ©jĂ un en-tĂȘte «grpc-accept-encoding» qui inclut le type de compression gzip. Le serveur lit cet en-tĂȘte et dĂ©termine que le client autorise le renvoi de rĂ©ponses compressĂ©es.
Une façon de visualiser l'effet de compression consiste Ă activer la journalisation pour notre application au moment de la conception. Cela peut ĂȘtre fait en modifiant le fichier
appsettings.Development.json
comme suit:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Grpc": "Trace",
"Microsoft": "Trace"
}
}
}
appsettings.Development.json GitHub
Lors du démarrage de notre serveur, nous obtenons des journaux de console beaucoup plus détaillés.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'
dbug: Grpc.AspNetCore.Server.ServerCallHandler[1]
Reading message.
dbug: Microsoft.AspNetCore.Server.Kestrel[25]
Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": started reading request body.
dbug: Microsoft.AspNetCore.Server.Kestrel[26]
Connection id "0HLQB6EMBPUIA", Request id "0HLQB6EMBPUIA:00000001": done reading request body.
trce: Grpc.AspNetCore.Server.ServerCallHandler[3]
Deserializing 0 byte message to 'Google.Protobuf.WellKnownTypes.Empty'.
trce: Grpc.AspNetCore.Server.ServerCallHandler[4]
Received message.
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
Serialized 'WeatherForecast.WeatherReply' to 2851 byte message.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 104 and flags END_HEADERS
trce: Grpc.AspNetCore.Server.ServerCallHandler[10]
Compressing message with 'gzip' encoding.
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
Message sent.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'gRPC - /WeatherForecast.WeatherForecasts/GetWeather'
trce: Microsoft.AspNetCore.Server.Kestrel[37]
Connection id "0HLQB6EMBPUIA" sending DATA frame for stream ID 1 with length 978 and flags NONE
trce: Microsoft.AspNetCore.Server.Kestrel[37]
Connection id "0HLQB6EMBPUIA" sending HEADERS frame for stream ID 1 with length 15 and flags END_STREAM, END_HEADERS
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 2158.9035ms 200 application/grpc
Log.txt GitHub
Sur la 16e ligne de ce journal, nous voyons que WeatherReply (en fait, un tableau de 100 éléments WeatherData dans cet exemple) a été sérialisé en protobuf et a une taille de 2851 octets.
Plus tard, à la ligne 20, nous voyons que le message a été compressé en utilisant le codage gzip, et à la ligne 26, nous pouvons voir la taille de la trame de données pour cet appel, qui est de 978 octets. Dans ce cas, les données ont été bien compressées (66%) car les éléments WeatherData répétitifs contiennent du texte et de nombreuses valeurs du message sont répétées.
Dans cet exemple, la compression gzip avait un bon effet sur la taille des données.
Désactiver la compression de réponse dans l'implémentation de la méthode de service
La compression de la rĂ©ponse peut ĂȘtre contrĂŽlĂ©e dans chaque mĂ©thode. Actuellement, j'ai trouvĂ© un moyen de le dĂ©sactiver. Lorsque la compression est activĂ©e pour un service ou un serveur, nous pouvons dĂ©sactiver la compression dans le cadre de l'implĂ©mentation de la mĂ©thode de service.
Jetons un coup d'Ćil au journal du serveur lors de l'appel d'une mĂ©thode de service qui transmet les messages WeatherData du serveur. Si vous souhaitez en savoir plus sur la diffusion en continu sur le serveur, vous pouvez lire mon article prĂ©cĂ©dent Diffusion de donnĂ©es sur un serveur avec gRPC et .NET Core .
info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
Sending WeatherData response
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
Serialized 'WeatherForecast.WeatherData' to 30 byte message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[10]
Compressing message with 'gzip' encoding.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
Connection id "0HLQBMRRH10JQ" sending DATA frame for stream ID 1 with length 50 and flags NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
Message sent.
Log.txt GitHub
Dans la 6Úme ligne, nous voyons que le message individuel WeatherData a une taille de 30 octets. La ligne 8 est compressée et la ligne 10 montre que les données font maintenant 50 octets - plus que le message d'origine. Dans ce cas, il n'y a aucun avantage pour nous de la compression gzip, nous constatons une augmentation de la taille totale du message envoyé sur le réseau.
Nous pouvons désactiver la compression pour un message spécifique en le configurant
WriteOptions
pour appeler une méthode de service.
public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
// ,
}
WeatherService.cs GitHub
Nous pouvons mettre
WriteOptions
au ServerCallContext
sommet de notre méthode de service. Nous passons dans une nouvelle instance WriteOptions
qui est WriteFlags
réglé sur NoCompress
. Ces paramÚtres sont utilisés pour l'entrée suivante.
Lors de la diffusion en continu des rĂ©ponses, cette valeur peut Ă©galement ĂȘtre dĂ©finie sur
IServerStreamWriter
.
public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
//
}
WeatherService.cs GitHub
Lorsque nous utilisons ce paramÚtre, les journaux montrent qu'aucune compression n'est appliquée aux appels à cette méthode de service.
info: WeatherForecast.Grpc.Server.Services.WeatherService[0]
Sending WeatherData response
dbug: Grpc.AspNetCore.Server.ServerCallHandler[6]
Sending message.
trce: Grpc.AspNetCore.Server.ServerCallHandler[9]
Serialized 'WeatherForecast.WeatherData' to 30 byte message.
trce: Microsoft.AspNetCore.Server.Kestrel[37]
Connection id "0HLQBMTL1HLM8" sending DATA frame for stream ID 1 with length 35 and flags NONE
trce: Grpc.AspNetCore.Server.ServerCallHandler[7]
Message sent.
Log.txt GitHub
Désormais, un message de 30 octets a une longueur de 35 octets dans la trame DATA. Il y a une petite surcharge de 5 octets supplémentaires dont nous n'avons pas à nous soucier ici.
Désactiver la compression de réponse du client GRPC
Par dĂ©faut, un canal gRPC inclut des paramĂštres qui dĂ©terminent les encodages qu'il accepte. Ceux-ci peuvent ĂȘtre configurĂ©s lors de la crĂ©ation d'un canal si vous souhaitez dĂ©sactiver la compression des rĂ©ponses de votre client. En gĂ©nĂ©ral, j'Ă©viterais cela et laisserais le serveur dĂ©cider quoi faire, car il sait mieux ce qui peut et ne peut pas ĂȘtre compressĂ©. Cependant, vous devrez parfois surveiller cela Ă partir du client.
Le seul moyen que j'ai trouvé dans mes recherches sur l'API à ce jour est de configurer un canal en passant une instance
GrpcChannelOptions
. L'une des propriétés de cette classe est pour CompressionProviders
- IList<
ICompressionProvider>
. Par défaut, lorsque cette valeur est nulle, l'implémentation client ajoute automatiquement un fournisseur de compression Gzip. Cela signifie que le serveur peut utiliser gzip pour compresser les messages de réponse, comme nous l'avons vu.
private static async Task Main()
{
using var channel = GrpcChannel.ForAddress("https://localhost:5005", new GrpcChannelOptions { CompressionProviders = new List<ICompressionProvider>() });
var client = new WeatherForecastsClient(channel);
var reply = await client.GetWeatherAsync(new Empty());
foreach (var forecast in reply.WeatherData)
{
Console.WriteLine($"{forecast.DateTimeStamp.ToDateTime():s} | {forecast.Summary} | {forecast.TemperatureC} C");
}
Console.WriteLine("Press a key to exit");
Console.ReadKey();
}
Program.cs GitHub
Dans cet exemple de code client, nous définissons
GrpcChannel
et poussons une nouvelle instance GrpcChannelOptions
. Nous attribuons CompressionProviders
une liste vide Ă la propriĂ©tĂ© . Puisque nous ne spĂ©cifions plus de fournisseurs dans notre canal lorsque les appels sont crĂ©Ă©s et envoyĂ©s via ce canal, ils n'incluront aucun encodage de compression dans l'en-tĂȘte grpc-accept-encoding. Le serveur voit cela et ne gzip pas la rĂ©ponse.
Résumé
Dans cet article, nous avons explorĂ© la possibilitĂ© de compresser les messages de rĂ©ponse du serveur gRPC. Nous avons constatĂ© que dans certains cas (mais pas tous), cela peut conduire Ă une charge utile plus petite. Nous avons vu que par dĂ©faut, les appels client incluent la valeur gzip "grpc-accept-encoding" dans les en-tĂȘtes. Si le serveur est configurĂ© pour appliquer la compression, il ne le fera que si le type de compression pris en charge correspond Ă l'en-tĂȘte de la demande.
Nous pouvons configurer
GrpcChannelOptions
lors de la création d'un canal pour le client pour désactiver la compression gzip. Sur le serveur, nous pouvons configurer l'ensemble du serveur à la fois ou un service distinct pour compresser les réponses. Nous pouvons également remplacer et désactiver cela au niveau de chaque méthode de service.
Pour en savoir plus sur gRPC, vous pouvez lire tous les articles qui font partie de monSĂ©rie gRPC et ASP.NET Core .
TOUT SUR LE COURS