gRPC for .NET Coreを試して統合サポートのありがたみを感じた

.NET Core 3.0にgRPCのサポートが追加された。

元々gRPCのC#対応はされていたはず、何が違うのか?答えはgRPC公式のブログに書いてあった。

https://grpc.io/blog/grpc-on-dotnetcore/

Unlike the existing C-Core based implementation (Grpc.Core), the new libraries (grpc-dotnet) make use of the existing networking primitives in the .NET Core Base Class Libraries (BCL). The diagram below highlights the difference between the existing Grpc.Core library and the new grpc-dotnet libraries.

f:id:castaneai:20200227170739p:plain
https://grpc.io/blog/grpc-on-dotnetcore/ より引用

つまり、元々Cで書かれたgRPC Core libraryのラッパーとしてのC#版は存在したが、今回のgRPC for .NET Coreはそれを使っていない。 .NET Coreの範囲内だけで実装されたgRPCということで、すっきり!

実際にgRPCプロジェクトを作った

なんと、次のコマンドひとつでgRPC(が統合されたASP.NET Core)プロジェクトが作れる(すごい)。

$ dotnet new grpc

するとこのようなプロジェクトが生成される。

.
├── appsettings.Development.json
├── appsettings.json
├── HelloDotNetGRPC.csproj
├── obj
│  ├── HelloDotNetGRPC.csproj.nuget.cache
│  ├── HelloDotNetGRPC.csproj.nuget.dgspec.json
│  ├── HelloDotNetGRPC.csproj.nuget.g.props
│  ├── HelloDotNetGRPC.csproj.nuget.g.targets
│  └── project.assets.json
├── Program.cs
├── Properties
│  └── launchSettings.json
├── Protos
│  └── greet.proto
├── Services
│  └── GreeterService.cs
└── Startup.cs

Greeterという最小限のサービスのサンプルまで一緒に作ってくれてる、便利!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Logging;

namespace HelloDotNetGRPC
{
    public class GreeterService : Greeter.GreeterBase
    {
        private readonly ILogger<GreeterService> _logger;
        public GreeterService(ILogger<GreeterService> logger)
        {
            _logger = logger;
        }

        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloReply
            {
                Message = "Hello " + request.Name
            });
        }
    }
}

Greeter.GreeterBase を継承しているが、このGreeterBaseのファイルは見当たらない。このままではビルドできないのでは?とか最初思った。

ビルドした

ビルドできないのでは?と思いつつビルドしたら普通に通った。なにそれすごい

$ dotnet build

...
ビルドに成功しました。
    0 個の警告
    0 エラー

ファイル一覧を確認してみると、obj/以下に色々なものが追加で生成されていた。

.
├── appsettings.Development.json
├── appsettings.json
├── bin
│  └── Debug
│     └── netcoreapp3.0
│        ├── appsettings.Development.json
│        ├── appsettings.json
│        ├── Google.Protobuf.dll
│        ├── Grpc.AspNetCore.Server.ClientFactory.dll
│        ├── Grpc.AspNetCore.Server.dll
│        ├── Grpc.Core.Api.dll
│        ├── Grpc.Net.Client.dll
│        ├── Grpc.Net.ClientFactory.dll
│        ├── Grpc.Net.Common.dll
│        ├── HelloDotNetGRPC
│        ├── HelloDotNetGRPC.deps.json
│        ├── HelloDotNetGRPC.dll
│        ├── HelloDotNetGRPC.pdb
│        ├── HelloDotNetGRPC.runtimeconfig.dev.json
│        ├── HelloDotNetGRPC.runtimeconfig.json
│        └── Properties
│           └── launchSettings.json
├── HelloDotNetGRPC.csproj
├── obj
│  ├── Debug
│  │  └── netcoreapp3.0
│  │     ├── 1255d1a520d30ea4_greet.protodep
│  │     ├── Greet.cs
│  │     ├── GreetGrpc.cs
│  │     ├── HelloDotNetGRPC
│  │     ├── HelloDotNetGRPC.AssemblyInfo.cs
│  │     ├── HelloDotNetGRPC.AssemblyInfoInputs.cache
│  │     ├── HelloDotNetGRPC.assets.cache
│  │     ├── HelloDotNetGRPC.csproj.CopyComplete
│  │     ├── HelloDotNetGRPC.csproj.FileListAbsolute.txt
│  │     ├── HelloDotNetGRPC.csprojAssemblyReference.cache
│  │     ├── HelloDotNetGRPC.dll
│  │     ├── HelloDotNetGRPC.MvcApplicationPartsAssemblyInfo.cache
│  │     ├── HelloDotNetGRPC.pdb
│  │     ├── HelloDotNetGRPC.RazorTargetAssemblyInfo.cache
│  │     └── staticwebassets
│  │        ├── HelloDotNetGRPC.StaticWebAssets.Manifest.cache
│  │        └── HelloDotNetGRPC.StaticWebAssets.xml
│  ├── HelloDotNetGRPC.csproj.nuget.cache
│  ├── HelloDotNetGRPC.csproj.nuget.dgspec.json
│  ├── HelloDotNetGRPC.csproj.nuget.g.props
│  ├── HelloDotNetGRPC.csproj.nuget.g.targets
│  └── project.assets.json
├── Program.cs
├── Properties
│  └── launchSettings.json
├── Protos
│  └── greet.proto
├── Services
│  └── GreeterService.cs
└── Startup.cs

なんと、さっき存在しなかったGreeterBaseがobj/以下に生成されていた。

つまり、gRPCのprotocによるコード生成をビルド時に内部で勝手にやってくれている。

自分が初めてGoでgRPCに入門したときはprotocを入れて、protoc-gen-goを入れて…何やら複雑なprotocコマンドを打って、、、ってやっていたので、ここまで何も考えずにスッとコード生成ができるのは本当にすごい!

もちろん、GoでもMakefileを書けば同じようなことは実現できるのだが、標準でこれがついてるというのが大きいと思う。 gRPCがシームレスに統合されたってどこかの記事に書いてたけど、こういうことか、すごいな〜。とありがたみを感じた。

ただ、同時に内部でこういうことをやっていると知らないままだと、トラブル起きたときに調査するのが難しくなりそうという反面もある。 このあたりは、Goは内部的な細かいステップもちゃんと意識させる世界観で、.NET Coreはエコシステムに全部乗っけてしまって内部でいい感じにしてくれる世界観という違いなのかもしれない。