gRPCãšASP.NETCoreã«é¢ãã ç§ã®ã·ãªãŒãºã®ãã®ãšããœãŒãã§ã¯ãgRPCãµãŒãã¹ã®å¿çå§çž®æ©èœãæ¥ç¶ããæ¹æ³ãèŠãŠãããŸãã
泚ïŒãã®èšäºã§ã¯ãåŒã³åºãã®èšå®ãšã¡ãœããã«ã€ããŠåŠç¿ããŠåŠãã å§çž®ã®è©³çްã®äžéšã«ã€ããŠèª¬æããŸããåãçµæãéæããããã®ããæ£ç¢ºã§ãã广çãªã¢ãããŒããããå¯èœæ§ããããŸãã
ãã®èšäºã¯ãgRPCãšASP.NETCoreã«é¢ããã·ãªãŒãºã®äžéšã§ãã
GRPCã§å§çž®ãæå¹ã«ããå¿ èŠãããã®ã¯ãã€ã§ããïŒ
ç°¡åãªçãïŒããã¯ããªãã®ãã€ããŒãã«äŸåããŸãã
é·ãçãïŒ
gRPCã¯ããããã¯ãŒã¯ãä»ããŠéä¿¡ãããèŠæ±ã¡ãã»ãŒãžãšå¿çã¡ãã»ãŒãžãã·ãªã¢ã«åããããã®ããŒã«ãšããŠãããã³ã«ãããã¡ãŒã䜿çšããŸãããããã³ã«ãããã¡ã¯ãããã©ã«ãã§å°ãããŠå¹ççãªãã€ããŒãçšã«èšèšããããã€ããªã·ãªã¢ã«å圢åŒãäœæããŸããéåžžã®JSONãã€ããŒããšæ¯èŒããŠãprotobufã¯ããæ§ãããªã¡ãã»ãŒãžãµã€ãºãæäŸããŸãã JSONã¯éåžžã«åé·ã§èªã¿ãããã§ãããã®çµæããããã¯ãŒã¯ãä»ããŠéä¿¡ãããããŒã¿ã«ããããã£åãå«ãŸããããã転éããå¿ èŠã®ãããã€ãæ°ãå¢å ããŸãã
ãããã³ã«ãããã¡ã¯ããããã¯ãŒã¯ãä»ããŠéä¿¡ãããããŒã¿ã®èå¥åãšããŠæŽæ°ã䜿çšããŸããããã¯ãããŒã¹128ããªã¢ã³ãã®æŠå¿µã䜿çšããŠããã0ã127ã®å€ãæã€ãã£ãŒã«ãã転éã«1ãã€ãã®ã¿ãå¿ èŠãšããããšãå¯èœã«ããŸããå€ãã®å Žåãã¡ãã»ãŒãžããã®ç¯å²ã®ãã£ãŒã«ãã«å¶éããããšãã§ããŸãã倧ããªæŽæ°ã«ã¯è€æ°ã®ãã€ããå¿ èŠã§ãã
ãããã£ãŠãprotobufãã€ããŒãã¯ããããã¯ãŒã¯ãä»ããŠéä¿¡ããããã€ããå¯èœãªéãæå°ã®ãµã€ãºã«åæžããããšãç®çãšããŠããããããã§ã«éåžžã«å°ããããšãå¿ããªãã§ãã ããããã ããGZipãªã©ã®åœ¢åŒã䜿çšãããšãããã«æå€±ã®ãªãå§çž®ãè¡ãããå¯èœæ§ããããŸãããã€ããŒãã«å§çž®ã®æ©æµãåããã®ã«ååãªå埩ããã¹ãããŒã¿ãããå Žåã«ã®ã¿ãµã€ãºã®çž®å°ãèŠãããããããã®å¯èœæ§ããã€ããŒãã§ãã¹ãããå¿ èŠããããŸããããããå°ããªå¿çã¡ãã»ãŒãžã®å Žåãããããå§çž®ããããšãããšãå§çž®ãããŠããªãã¡ãã»ãŒãžã䜿çšãããããå€ãã®ãã€ããçºçããå¯èœæ§ããããŸããããã¯æããã«è¯ããããŸããã
ãŸããããã»ããµã®å§çž®ãªãŒããŒããããæ³šç®ã«å€ããŸããããã¯ããµã€ãºã®çž®å°ããåŸãããå©çãäžåãå¯èœæ§ããããŸãããµãŒãã¹ã®å šäœåãææ¡ããã«ã¯ãå§çž®ã¬ãã«ã倿ŽããåŸãèŠæ±ã®CPUãšã¡ã¢ãªã®ãªãŒããŒãããã远跡ããå¿ èŠããããŸãã
ASP.NET Core Server Integrationã¯ããã©ã«ãã§å§çž®ã䜿çšããŸãããããµãŒããŒå šäœãŸãã¯ç¹å®ã®ãµãŒãã¹ã«å¯ŸããŠå§çž®ãæå¹ã«ããããšãã§ããŸããããã¯ãããŸããŸãªã¡ãœããã®å¿çãçµæçã«è¿œè·¡ããããããå§çž®ããããšã®å©ç¹ãè©äŸ¡ã§ããããã劥åœãªããã©ã«ãã®ããã«æãããŸãã
GRPCã§å¿çå§çž®ãæå¹ã«ããã«ã¯ã©ãããã°ããã§ããïŒ
ãããŸã§ã®ãšãããgRPCå¿çå§çž®ãæ¥ç¶ããããã®2ã€ã®äž»èŠãªã¢ãããŒããèŠã€ããŸãããããããµãŒããŒã¬ãã«ã§æ§æããŠããã¹ãŠã®gRPCãµãŒãã¹ãå¿çã«å§çž®ãé©çšããããã«ããããåã ã®ãµãŒãã¹ã¬ãã«ã§æ§æããããšãã§ããŸãã
ãµãŒããŒã¬ãã«ã®æ§æ
services.AddGrpc(o =>
{
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
Startup.cs GitHub
AddGrpcå
éš
ã®ã¡ãœããã䜿çšããŠäŸåé¢ä¿ã€ã³ãžã§ã¯ã·ã§ã³ã³ã³ããã«gRPCãµãŒãã¹ãç»é²ããå Žåãã§ConfigureServicesæ§æããæ©äŒããããŸãGrpcServiceOptionsããã®ã¬ãã«ã§ã¯ããã©ã¡ãŒã¿ãŒã¯ãµãŒããŒãå®è£
ãããã¹ãŠã®gRPCãµãŒãã¹ã«åœ±é¿ããŸãã
æ¡åŒµã¡ãœããã®ãªãŒããŒããŒãã䜿çšããŠ
AddGrpcããæäŸã§ããŸã Action<GrpcServiceOptions>ãäžèšã®ã³ãŒãã¹ããããã§ã¯ããgzipãå§çž®ã¢ã«ãŽãªãºã ãéžæããŠããŸããCompressionLevelããŒã¿å§çž®ã®ããã«ç ç²ã«ããæéãæäœããŠããµã€ãºãå°ããããããšã«ãã£ãŠç¢ºç«ããããšãã§ããŸãããã©ã¡ãŒã¿ãæå®ãããŠããªãå ŽåãçŸåšã®å®è£
ã§ã¯ããã©ã«ãã§CompressionLevel.Fastestãã䜿çšããŸããåã®ã¹ããããã§ã¯ããã€ãæ°ãå¯èœãªéãæå°ã®ãµã€ãºã«æžããããã«ãå§çž®ã«ããå€ãã®æéãèš±å¯ããŸããã
ãµãŒãã¹ã¬ãã«ã®æ§æ
services.AddGrpc()
.AddServiceOptions<WeatherService>(o =>
{
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
Startup.cs GitHub
åŒã³åºã
AddGrpcã¯ãè¿ããŸãIGrpcServerBuilderããã«ããŒã§åŒã³åºãããæ¡åŒµã¡ãœãããåŒã³åºããŠãAddServiceOptionsåãµãŒãã¹ã®ãã©ã¡ãŒã¿ãŒãåå¥ã«æäŸã§ããŸãããã®ã¡ãœããã¯æ±çšã§ããããã©ã¡ãŒã¿ãŒãé©çšããå¿
èŠãããgRPCãµãŒãã¹ã®ã¿ã€ããåããŸãã
åã®äŸã§ã¯ãå®è£ ã«ãã£ãŠåŠçãããåŒã³åºãã®ãã©ã¡ãŒã¿ãŒãæäŸããããšãéžæããŸãã
WeatherServiceããã®ã¬ãã«ã§ã¯ããµãŒããŒã¬ãã«ã®æ§æã§èª¬æããã®ãšåããªãã·ã§ã³ã䜿çšã§ããŸãããã®ã·ããªãªã§ã¯ããã®ãµãŒããŒäžã®ä»ã®gRPCãµãŒãã¹ã¯ããã®ç¹å®ã®ãµãŒãã¹ã«èšå®ããå§çž®ãªãã·ã§ã³ãåãåããŸããã
GRPCã¯ã©ã€ã¢ã³ãã®ãªã¯ãšã¹ã
å¿çã®å§çž®ãæå¹ã«ãªã£ãã®ã§ãã¯ã©ã€ã¢ã³ããå§çž®ãããã³ã³ãã³ããåãå ¥ããŠããããšãèŠæ±ã瀺ããŠããããšã確èªããå¿ èŠããããŸããå®éãããã¯
GrpcChannelãäœæãããã¡ãœããForAddressã§äœ¿çšãããšããã©ã«ãã§æå¹ã«ãªã£ãŠãããããã¯ã©ã€ã¢ã³ãã³ãŒãã§äœãããå¿
èŠã¯ãããŸããã
var channel = GrpcChannel.ForAddress("https://localhost:5005");
Program.cs GitHub
ãã®æ¹æ³ã§äœæããããã£ãã«ã¯ãgzipå§çž®ã¿ã€ããå«ããgrpc-accept-encodingãããããŒããã§ã«éä¿¡ããŠããŸãããµãŒããŒã¯ãã®ããããŒãèªã¿åããã¯ã©ã€ã¢ã³ããå§çž®ãããå¿çã®è¿éãèš±å¯ããŠãããšå€æããŸãã
å§çž®å¹æãèŠèŠåãã1ã€ã®æ¹æ³ã¯ãèšèšæã«ã¢ããªã±ãŒã·ã§ã³ã®ãã®ã³ã°ãæå¹ã«ããããšã§ããããã¯
appsettings.Development.jsonãæ¬¡ã®ããã«ãã¡ã€ã«ã倿Žããããšã§å®è¡ã§ããŸãã
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Grpc": "Trace",
"Microsoft": "Trace"
}
}
}
appsettings.Development.json GitHub
ãµãŒããŒãèµ·åãããšããã詳现ãªã³ã³ãœãŒã«ãã°ã衚瀺ãããŸãã
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
ãã®ãã°ã®16è¡ç®ã«ãWeatherReplyïŒå®éã«ã¯ããã®äŸã§ã¯100åã®WeatherDataèŠçŽ ã®é åïŒãprotobufã«ã·ãªã¢ã«åãããŠããããµã€ãºã¯2851ãã€ãã§ããããšãããããŸãã
ãã®åŸã20è¡ç®ã§ãã¡ãã»ãŒãžãgzipãšã³ã³ãŒãã£ã³ã°ã䜿çšããŠå§çž®ãããŠããããšããããã26è¡ç®ã§ããã®åŒã³åºãã®ããŒã¿ãã¬ãŒã ãµã€ãºïŒ978ãã€ãïŒãããããŸãããã®å Žåãç¹°ãè¿ãããWeatherDataèŠçŽ ã«ã¯ããã¹ããå«ãŸããã¡ãã»ãŒãžå ã®å€ã®å€ããç¹°ãè¿ããããããããŒã¿ã¯é©åã«ïŒ66ïŒ ïŒå§çž®ãããŸããã
ãã®äŸã§ã¯ãgzipå§çž®ãããŒã¿ãµã€ãºã«è¯ã圱é¿ãäžããŸããã
ãµãŒãã¹ã¡ãœããã®å®è£ ã§å¿çå§çž®ãç¡å¹ã«ãã
å¿çã®å§çž®ã¯ãåæ¹æ³ã§å¶åŸ¡ã§ããŸããçŸåšãç§ã¯ããããªãã«ããæ¹æ³ãèŠã€ããŸããããµãŒãã¹ãŸãã¯ãµãŒããŒã§å§çž®ãæå¹ã«ãªã£ãŠããå ŽåããµãŒãã¹ã¡ãœããã®å®è£ ã®äžéšãšããŠå§çž®ããªããã¢ãŠãã§ããŸãã
ãµãŒããŒããWeatherDataã¡ãã»ãŒãžãéä¿¡ãããµãŒãã¹ã¡ãœãããåŒã³åºããšãã®ãµãŒããŒãã°ãèŠãŠã¿ãŸãããããµãŒããŒãžã®ã¹ããªãŒãã³ã°ã«ã€ããŠè©³ããç¥ãããå Žåã¯ãååã®èšäºãgRPCãš.NETCoreã䜿çšãããµãŒããŒãžã®ããŒã¿ã®ã¹ããªãŒãã³ã°ããåç §ããŠãã ããã
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
6è¡ç®ã§ã¯ãåã ã®WeatherDataã¡ãã»ãŒãžã®ãµã€ãºã30ãã€ãã§ããããšãããããŸãã8è¡ç®ã¯å§çž®ãããŠããã10è¡ç®ã¯ãããŒã¿ã®é·ããå ã®ã¡ãã»ãŒãžãã50ãã€ãé·ãããšã瀺ããŠããŸãããã®å Žåãgzipå§çž®ã«ããã¡ãªããã¯ãªãããããã¯ãŒã¯çµç±ã§éä¿¡ãããã¡ãã»ãŒãžã®åèšãµã€ãºãå¢å ããŸãããµãŒãã¹ã¡ãœãããåŒã³åºãããã«
èšå®
WriteOptionsããããšã§ãç¹å®ã®ã¡ãã»ãŒãžã®å§çž®ããªãã«ã§ããŸãã
public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
context.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
// ,
}
WeatherService.cs GitHub
ç§ãã¡ã¯ãèšå®ããããšãã§ããŸã
WriteOptionsã§ServerCallContextç§ãã¡ã®ãµãŒãã¹ã¡ãœããã®ããããç§ãã¡ã¯ãæ°ããã€ã³ã¹ã¿ã³ã¹ã«æž¡ããŠããWriteOptionsãWriteFlagsã«èšå®ããŸãNoCompressããããã®ãã©ã¡ãŒã¿ã¯ã次ã®ãšã³ããªã«äœ¿çšãããŸãã
å¿çãã¹ããªãŒãã³ã°ããå Žåããã®å€ãã«èšå®ããããšãã§ããŸã
IServerStreamWriterã
public override async Task GetWeatherStream(Empty _, IServerStreamWriter<WeatherData> responseStream, ServerCallContext context)
{
responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress);
//
}
WeatherService.cs GitHub
ãã®ãã©ã¡ãŒã¿ãŒã䜿çšãããšããã°ã«ã¯ããã®ãµãŒãã¹ã¡ãœããã®åŒã³åºãã«å§çž®ãé©çšãããŠããªãããšã瀺ãããŸãã
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
ããã§ã30ãã€ãã®ã¡ãã»ãŒãžã¯DATAãã¬ãŒã ã§35ãã€ãã®é·ãã«ãªããŸããããã§å¿é ããå¿ èŠã®ãªãäœåãª5ãã€ãã®å°ããªãªãŒããŒãããããããŸãã
GRPCã¯ã©ã€ã¢ã³ãããã®å¿çå§çž®ãç¡å¹ã«ãã
ããã©ã«ãã§ã¯ãgRPCãã£ãã«ã«ã¯ãåãå ¥ãããšã³ã³ãŒãã£ã³ã°ã決å®ãããã©ã¡ãŒã¿ãå«ãŸããŠããŸããã¯ã©ã€ã¢ã³ãããã®å¿çã®å§çž®ãç¡å¹ã«ããå Žåã¯ããã£ãã«ã®äœææã«ããããæ§æã§ããŸããäžè¬çã«ãå§çž®ã§ãããã®ãšã§ããªããã®ãããç¥ã£ãŠããã®ã§ããããé¿ããŠãµãŒããŒã«äœãããããæ±ºå®ãããŸãããã ããã¯ã©ã€ã¢ã³ããããããç£èŠããå¿ èŠãããå ŽåããããŸãã
ãããŸã§ã®API調æ»ã§ç§ãèŠã€ããå¯äžã®æ¹æ³ã¯ãã€ã³ã¹ã¿ã³ã¹ãæž¡ããŠãã£ãã«ãèšå®ããããš
GrpcChannelOptionsã§ãããã®ã¯ã©ã¹ã®ããããã£ã®1ã€ã¯CompressionProviders-çšIList<ICompressionProvider>ã§ããããã©ã«ãã§ã¯ããã®å€ãnullã®å Žåãã¯ã©ã€ã¢ã³ãå®è£
ã¯èªåçã«Gzipå§çž®ãããã€ããŒã远å ããŸããããã¯ããããŸã§èŠãŠããããã«ããµãŒããŒãgzipã䜿çšããŠå¿çã¡ãã»ãŒãžãå§çž®ã§ããããšãæå³ããŸãã
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
ãã®ãµã³ãã«ã¯ã©ã€ã¢ã³ãã³ãŒãã§ã¯
GrpcChannelãæ°ããã€ã³ã¹ã¿ã³ã¹ãèšå®ããŠããã·ã¥ããŠããŸãGrpcChannelOptionsãããããã£ã«CompressionProviders空ã®ãªã¹ããå²ãåœãŠãŠããŸããåŒã³åºããäœæããããã®ãã£ãã«ãä»ããŠéä¿¡ããããšãã«ãã£ãã«ã§ãããã€ããŒãæå®ããªããããgrpc-accept-encodingããããŒã«å§çž®ãšã³ã³ãŒãã£ã³ã°ã¯å«ãŸããŸããããµãŒããŒã¯ãããèªèããå¿çãgzipããŸããã
æŠèŠ
ãã®æçš¿ã§ã¯ãgRPCãµãŒããŒããã®å¿çã¡ãã»ãŒãžãå§çž®ããå¯èœæ§ã«ã€ããŠæ€èšããŸãããäžéšã®ïŒãã¹ãŠã§ã¯ãªãïŒã±ãŒã¹ã§ã¯ãããã«ãããã€ããŒããå°ãããªãå¯èœæ§ãããããšãããããŸãããããã©ã«ãã§ã¯ãã¯ã©ã€ã¢ã³ãåŒã³åºãã®ããããŒã«gzipå€ãgrpc-accept-encodingããå«ãŸããŠããããšãããããŸããããµãŒããŒãå§çž®ãé©çšããããã«æ§æãããŠããå ŽåããµããŒããããŠããå§çž®ã¿ã€ããèŠæ±ããããŒãšäžèŽããå Žåã«ã®ã¿é©çšãããŸããã¯ã©ã€ã¢ã³ãã®ãã£ãã«ãäœæãããšãã«ãgzipå§çž®ãç¡å¹ã«ãã
ããã«æ§æã§ããŸã
GrpcChannelOptionsããµãŒããŒã§ã¯ããµãŒããŒå
šäœãäžåºŠã«æ§æããããšããå¥ã®ãµãŒãã¹ãæ§æããŠå¿çãå§çž®ããããšãã§ããŸããåãµãŒãã¹ã¡ãœããã®ã¬ãã«ã§ããããªãŒããŒã©ã€ãããã³ç¡å¹ã«ããããšãã§ããŸãã
gRPCã®è©³çްã«ã€ããŠã¯ãç§ã®äžéšã§ãããã¹ãŠã®èšäºãèªãããšãã§ããŸãgRPCããã³ASP.NETã³ã¢ã·ãªãŒãºã
ã³ãŒã¹ã®ãã¹ãŠ