.NET Core 3.0 で gRPC がシームレスに統合されて素晴らしい件

こんにちは、アーキテクトの小林です。

.NET Core 3.0 がついに GA になりました。
.NET Core 3.0 では gRPC の機能サポートが組み込まれています。

今回は .NET Core 3.0 で簡単に gRPC の開発をスタートすることができることを知っていただいきたいと思いまして、gRPC サーバーとクライアントの疎通確認ができるまでの手順を記事にしてみました。

f:id:ecb_mkobayashi:20190924113406p:plain

そもそも gRPC とは何?

gRPC は、Google によって開発されたスキーマファーストの RPC(リモートプロシージャーコール)フレームワークで、マイクロサービスアーキテクチャにおけるサービス間通信において有力な選択肢として注目されている技術です。

マイクロサービスアーキテクチャでは、大きなシステムを細かいサービスに分割し、各サービスを独立させ、疎結合に保つことが重視されます。細かいサービスに分割した結果として「あちらを立てればこちらが立たず」といういくつかの問題が発生してしまうことになりました。

ドキュメンテーションに関する問題

gRPC が注目される理由の一つが API のドキュメンテーションに関する問題です。


マイクロサービスアーキテクチャでは、サービス間の通信は API を RPC によって緩く結合させます。このとき、API のドキュメントは API を使う側にとって重要な情報源となります。


たとえば .NET のモノリシックなアプリケーションであれば、各メソッドにサマリコメントを記述しておけばコーディング中にインテリセンスで必要な情報が得られていたわけですから、メソッドを作った側と使う側の情報の受け渡しが非常にスムーズでした。


しかし、これが REST API である場合、自前でドキュメントを準備するか、Swagger のようなツールを使ってドキュメントを作成しなければなりません。REST API はオープンな技術の上に成り立っていますので、設計者に委ねられる部分が多く存在します。たとえば、POST なのか PUT なのか、XML なのか JSON なのか form-urlencoded なのか。これらは設計者によってドキュメントが提供されている必要があり、ドキュメントが無ければ API を使う側は何も進めることができないのです。そしてドキュメント通りに実装されているのは当然のことながら、API の変更のたびにドキュメントの維持管理も必要になりました。

f:id:ecb_mkobayashi:20190917165315p:plain
REST API ではドキュメントがないと開発を進めることができません

各サービスが独立して疎結合になって、おのおのの開発チームがスピーディに開発・デプロイできるようになったことと引き換えに、開発チームには今までよりもいっそう細かいレベルのドキュメンテーションを課してしまうことになってしまいました。これはドキュメントよりも動くコードを重視するアジャイルな開発プロセスにとってオーバーヘッドであることは間違いありません。


gRPC は、スキーマファーストというアプローチによってドキュメントと実装を完全に一致させたフレームワークです。どういうことかというと、スキーマを定義すればサーバー側の実装スケルトン(抽象クラス)が生成され、使う側クライアントの実装が出来上がるのです。


gRPC には、スキーマファイルに記述された内容から読みやすいドキュメントを生成するツール(protoc-gen-doc)も開発されていて、ドキュメンテーションに関する問題を一気通貫に解決してくれる技術であると思います。

f:id:ecb_mkobayashi:20190917182321p:plain
gRPC ならスキーマファイルが仕様書であり自動生成されるコードを使えば大丈夫です

性能に関する問題

gRPC が注目される理由のもう一つがマイクロサービスアーキテクチャにおける性能に関する問題です。


マイクロサービスアーキテクチャでは、各サービス間の通信を API の RPC によって緩く結合していきますが、この RPC の速度は全体の性能に大きく影響します。RPC は、ネットワークを超えたプロセス間通信になりますので、同じアプリケーションプロセスのメモリ内に配置されたメソッド呼び出しとは次元が違うレベルで遅くなってしまいます。


gRPC は、データ転送時のシリアル化において Protobuf (プロトコルバッファー)という軽量で超高速な技術を使います。Protobuf と XML を比較するとサイズは3倍~10倍軽量になり、速度は20倍~100倍も速くなるということです。


[参考]
https://developers.google.com/protocol-buffers/docs/overview


シリアル化と逆シリアル化は1回の通信で最大4回も行われていますので、このオーバーヘッドは無視できません。

f:id:ecb_mkobayashi:20190919172336p:plain

Google は検索エンジン市場の黎明期、速さによって市場を手中に収めましたが、Protobuf はその Google における全てのマシン間通信で採用されている技術とのこと。速さは正義ですね。


さて、gRPCの良い点ばかりを紹介してきましたが、短所もあります。
それは、ブラウザから直接利用できないことと、通信内容がバイナリなので人が判読できないという点です。MSのドキュメントに特徴が比較されているページがありましたので、詳しく知りたい方はご参照ください。

docs.microsoft.com

.NET Core 3.0 は gRPC をファーストクラスサポート

そんなマイクロサービスアーキテクチャにおける期待の星である gRPC が .NET Core 3.0 にてファーストクラスのサポートを得て、.NET Core にシームレスに統合されました。.NET Core 3.0 以前はどうだったのかというと、gRPC のツールを起動してサーバー・クライアント実装を生成するコマンドを実行する必要がありました。.NET Core 3.0 になってから、スキーマファイルを記述して、プロジェクトファイル(.csproj)にわずかな記述を追記するだけで、ビルド時に透過的に実装を生成してくれるようになりました。


また、Kestrel サーバーに対して gRPC サーバーを簡単にホストできるようになっており、Visual Studio には gRPC のプロジェクトテンプレートも提供されました。極めて簡単に gRPC の開発をスタートすることができるようになっています。.NET Core ですから gRPC サーバーを Docker コンテナにすることで、AWS、Azure を問わずクラウド上で実行することも可能です。

.NET Core 3.0 と Visual Studio 2019 Community のインストール

前置きがずいぶんと長くなってしまいましたが、ここからは gRPC のサーバーとクライアントの開発のはじめ方を手順を追ってご紹介していきたいと思います。

最初に .NET Core 3.0 SDK をインストールする必要があります。
環境に合わせて SDK インストーラーをダウンロードしてインストールしてください。


.NET Core SDK
https://dotnet.microsoft.com/download/dotnet-core/3.0


次に Visual Studio 2019 Community をインストールします。Visual Studio Code でも開発は可能ですが、はじめて開発する場合は Visual Studio 2019 の方がわかりやすいのでおすすめです。
Visual Studio 2019 Community であれば個人の学習目的であれば無償ですし、条件を満たせば商用アプリケーションの開発にも利用できます。


Visual Studio 2019 Community
https://visualstudio.microsoft.com/ja/vs/


インストール時にワークロードを選択する必要があります。gRPC の開発に必要なワークロードは「ASP.NETとWeb 開発」、「Azureの開発」、「.NET Core クロスプラットフォームの開発」、「.NET デスクトップ開発」あたりを選んでおけばだいたい OK かと思います。

gRPC サーバー開発のはじめかた

Visual Studio 2019 起動したら「新しいプロジェクトの作成」をクリックします。
f:id:ecb_mkobayashi:20190917190108p:plain

下にスクロールすると「gRPCサービス」が新しく追加されています!
f:id:ecb_mkobayashi:20190917190231p:plain

プロジェクトを配置するディレクトリを用意して、プロジェクト名、場所、ソリューション名を入力して作成をクリックします。
f:id:ecb_mkobayashi:20190917190359p:plain

プロジェクトテンプレートは今は一つしかありません。
画面右に Docker サポートのオプションがありますが、Dockerfile はあとで手動で用意すればよいのでそのまま作成をクリックします。
f:id:ecb_mkobayashi:20190917191124p:plain

まずはソリューションをビルドしてみて正常にビルドできることを確認します。
f:id:ecb_mkobayashi:20190917191208p:plain

つづいてF5キーを押してデバッグ実行を開始します。
ここで証明書の確認ダイアログが出ました。
gRPC は HTTP/2 が前提となっています。さらに HTTP/2 は SSL 証明書が前提になっているため、 自己署名証明書を Visual Studio が自動的に生成して必要な設定を行ってくれます。ここは両方とも「はい」と答えます。
f:id:ecb_mkobayashi:20190917191448p:plain
f:id:ecb_mkobayashi:20190917191509p:plain

デバッグ実行が開始される際にエラーが出てしまいました。証明書は「はい」と答えたのですが、うまく設定できなかったみたいです。

crit: Microsoft.AspNetCore.Server.Kestrel[0] 
      Unable to start Kestrel. 
System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date. 
To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'. 
For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054. 


エラーメッセージによると、dotnet dev-certs https --trust を実行すると良さそうなのですが実行してもうまくいきませんでした。

helpコマンドで調べてみると妥当性のチェックができるらしいので、さっそくチェックしてみると「有効な証明書が見つかりません」と言われてしまいました。

dotnet dev-certs https --check 
No valid certificate found. 

この問題を解決してくれそうな雰囲気の clean コマンドがあったので一旦削除してみました。

dotnet dev-certs https --clean 
Cleaning HTTPS development certificates from the machine. A prompt might get displayed to confirm the removal of some of the certificates. 
HTTPS development certificates successfully removed from the machine. 

もう一度証明書を作成してみます。

dotnet dev-certs https --trust 
Trusting the HTTPS development certificate was requested. A confirmation prompt will be displayed if the certificate was not previously trusted. Click yes on the prompt to trust the certificate. 
The HTTPS developer certificate was generated successfully. 


うまくいったみたいです。有効な証明書として認識されていることが確認できました。

dotnet dev-certs https --check 
A valid certificate was found. 


これで無事にデバッグ実行できるようになりました。

info: Microsoft.Hosting.Lifetime[0] 
      Now listening on: https://localhost:5001 
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down. 
info: Microsoft.Hosting.Lifetime[0] 
      Hosting environment: Development 
info: Microsoft.Hosting.Lifetime[0] 
      Content root path: D:\Work\dotnetcore\MyFirstGrpc\src\MyFirstGrpc.Grpc 

無事に gRPC サーバーが立ち上がったので、クライアントをつくってみましょう。

gRPC クライアント開発のはじめかた

引き続き、現在のソリューションに gRPC クライアントプロジェクトを追加します。
ファイルメニューの「追加」から「新しいプロジェクト」を選択して、「コンソールアプリ」を選択します。
f:id:ecb_mkobayashi:20190924092342p:plain

プロジェクト名を入力します。
f:id:ecb_mkobayashi:20190924092425p:plain

プロジェクトが作成できたらソリューションエクスプローラーのクライアントプロジェクトを右クリックして、「NuGet パッケージの管理」から以下の3つのパッケージを追加します。

  • Grpc.Net.Client
  • Google.Protobuf
  • Grpc.Tools

f:id:ecb_mkobayashi:20190924092807p:plain

スキーマファイル(.proto)はサーバーとクライアントで共通の定義ファイルを使用します。
サーバー側で作成されている Protos フォルダをクライアント側にドラッグアンドドロップでコピーします。
f:id:ecb_mkobayashi:20190924093000p:plain
f:id:ecb_mkobayashi:20190924093010p:plain

クライアントのアプリケーションのプロジェクトファイル(csproj)を開いて編集します。
ドラッグアンドドロップで追加したスキーマファイルは自動的にサーバー実装のコードを吐き出す設定になるようでした。これを Client に書き換えます。

<ItemGroup> 
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" /> 
</ItemGroup><ItemGroup> 
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" /> 
</ItemGroup> 

Program.csファイルは以下のような記述に変更します。

using System; 
using MyFirstGrpc.Grpc;                   // ここは .proto ファイルに記載された名前
using Grpc.Net.Client; 
using System.Threading.Tasks; 

namespace MyFirstGrpc.Client 
{ 
    class Program 
    { 
        static async Task Main(string[] args) 
        { 
            // The port number(5001) must match the port of the gRPC server. 
            var channel = GrpcChannel.ForAddress("https://localhost:5001"); 
            var client = new Greeter.GreeterClient(channel); 
            var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" }); 
            Console.WriteLine("Greeting: " + reply.Message); 
            Console.WriteLine("Press any key to exit..."); 
            Console.ReadKey(); 
        } 
    } 
} 

これでサーバーとクライアンドの準備が完了しました。
まずは、MyFirstGrpc.Grpc プロジェクトをスタートアッププロジェクトにして Ctrl +F5 (デバッグなしで開始)します。
その後、MyFirstGrpc.Clientプロジェクトをスタートアッププロジェクトにしてデバッグ実行を開始してみましょう。無事にサーバーの応答メッセージが表示されました!

Greeting: Hello GreeterClient 
Press any key to exit... 

ここまでの手順の振り返り

簡単な手順でサーバーとクライアントの gRPC 通信環境ができあがりました。
まず、gRPC のサーバー開発はプロジェクトテンプレートが提供されていて、ミニマムな構成のソースコードからスタートできるようになっています。

サーバー開発のプロジェクトテンプレートに含まれているファイルは以下の通りです。

  • \Properties\launchSettings.json 開発環境における起動設定情報です。稼働環境には必要ありません。
  • \Protos\greet.proto スキーマファイルです。gRPC の勉強をして記法を学びましょう。インテリセンスが効きます。
  • \Services\GreeterService.cs サービスの実装クラスです。greet.proto で自動生成される抽象クラスを継承しています。
  • appsettings.json 設定ファイルです。環境を問わない共通の設定を記述します。
  • appsettings.Development.json 設定ファイルです。環境変数 ASPNETCORE_ENVIRONMENT が Development のときに appsettings.json の設定情報を上書きします。
  • Program.cs アプリケーションのエントリポイントとなる Main 関数を備えたクラスです。
  • Startup.cs gRPC サーバーのスタートアップコードを記述するクラスです。DI の設定やルーティングの設定を記述します。

.NET Core 3.0 の gRPC は、スキーマファイルから動的にプロキシコードを生成してくれています。生成したプロキシコードは obj フォルダ内であり、プロキシコードがプロジェクトに追加されることはなく、一般的な .NET 開発の .gitignore であればソースコード管理に入ってしまうこともありません。

プロキシコードはエディターから該当クラス(ここでは Greeter.GreeterBase )を右クリックして「定義へ移動」と選択すると、 プロキシコードも閲覧することもできます。

/// <summary>Base class for server-side implementations of Greeter</summary>
[grpc::BindServiceMethod(typeof(Greeter), "BindService")]
public abstract partial class GreeterBase
{
  /// <summary>
  /// Sends a greeting
  /// </summary>
  /// <param name="request">The request received from the client.</param>
  /// <param name="context">The context of the server-side call handler being invoked.</param>
  /// <returns>The response to send back to the client (wrapped by a task).</returns>
  public virtual global::System.Threading.Tasks.Task<global::MyFirstGrpc.Grpc.HelloReply> SayHello(global::MyFirstGrpc.Grpc.HelloRequest request, grpc::ServerCallContext context)
  {
    throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
  }
}

クライアントの gRPC は、3つの NuGet パッケージを追加してからわずかな手順で gRPC サーバーを呼び出すことが可能になりました。スキーマファイルをプロジェクトに投げ込むだけで、すぐに呼び出しできるという手軽さを実感できました。

まとめ

.NET Core 3.0 の gRPC はシームレスに統合されていて、アプリケーションの実装部分に集中すれば良いという環境が整っています。昨今のマイクロサービスアーキテクチャへのパラダイムシフトを強力にバックアップしてくれそうな gRPC と、それを見事に組み込んだ .NET Core に期待感が半端ない!

今回は長くなってしまったので、Docker のコンテナー化とクラウドへのデプロイは次回に持ち越しすることにしました。次回の記事では今回の gRPC サービスを Docker でコンテナー化して、Azure にホストするところまで進めてみたいと思います。

それでは!



~ecbeingでは .NET Core とマイクロサービスアーキテクチャに注目している、イケてるエンジニアを大募集中です!~
careers.ecbeing.tech