gRPCとREST:最高のAPIプロトコル入門

公開: 2022-07-22

今日のテクノロジー環境では、ほとんどのプロジェクトでAPIを使用する必要があります。 APIは、単一の複雑なシステムを表す可能性があるが、別々のマシンに存在する場合や、互換性のない複数のネットワークまたは言語を使用する場合があるサービス間の通信をブリッジします。

多くの標準テクノロジーは、REST、SOAP、GraphQL、gRPCなどの分散システムのサービス間通信のニーズに対応しています。 RESTは好まれるアプローチですが、gRPCは価値のある候補であり、高性能、型付きコントラクト、および優れたツールを提供します。

RESTの概要

Representational State Transfer(REST)は、サービスのデータを取得または操作する手段です。 REST APIは通常、HTTPプロトコルに基づいて構築され、URIを使用してリソースを選択し、HTTP動詞(GET、PUT、POSTなど)を使用して目的の操作を選択します。 リクエストとレスポンスの本文には操作に固有のデータが含まれ、ヘッダーはメタデータを提供します。 説明のために、RESTAPIを介して製品を取得する簡単な例を見てみましょう。

ここでは、IDが11の製品リソースをリクエストし、APIにJSON形式で応答するように指示します。

 GET /products/11 HTTP/1.1 Accept: application/json

このリクエストを考えると、私たちの応答(無関係なヘッダーは省略)は次のようになります。

 HTTP/1.1 200 OK Content-Type: application/json { id: 11, name: "Purple Bowtie", sku: "purbow", price: { amount: 100, currencyCode: "USD" } }

JSONは人間が読める形式ですが、サービス間で使用する場合は最適ではありません。 プロパティ名を参照することは、圧縮されている場合でも繰り返し発生するため、メッセージが肥大化する可能性があります。 この懸念に対処するための代替案を見てみましょう。

gRPCの概要

gRPC Remote Procedure Call(gRPC)は、オープンソースの契約ベースのクロスプラットフォーム通信プロトコルであり、一連の機能を外部クライアントに公開することにより、サービス間通信を簡素化および管理します。

HTTP / 2の上に構築されたgRPCは、双方向ストリーミングや組み込みのトランスポート層セキュリティ(TLS)などの機能を活用します。 gRPCは、シリアル化されたバイナリペイロードを介してより効率的な通信を可能にします。 RESTがJSONを使用するのと同様に、構造化データのシリアル化のメカニズムとしてデフォルトでプロトコルバッファを使用します。

ただし、JSONとは異なり、プロトコルバッファはシリアル化された形式以上のものです。 それらには、他の3つの主要な部分が含まれます。

  • .protoファイルにあるコントラクト定義言語(最新のプロトコルバッファ言語仕様であるproto3に従います)。
  • 生成されたアクセサ機能コード
  • 言語固有のランタイムライブラリ

サービスで使用できるリモート機能( .protoファイルで定義)は、プロトコルバッファファイルのサービスノード内に一覧表示されます。 開発者として、プロトコルバッファのリッチタイプシステムを使用して、これらの関数とそのパラメータを定義することができます。 このシステムは、入力メッセージと出力メッセージを定義するために、さまざまな数値と日付のタイプ、リスト、辞書、およびnullableをサポートしています。

これらのサービス定義は、サーバーとクライアントの両方で使用できる必要があります。 残念ながら、 .protoファイル自体への直接アクセスを提供する以外に、これらの定義を共有するデフォルトのメカニズムはありません。

この例の.protoファイルは、IDを指定して製品エントリを返す関数を定義しています。

 syntax = "proto3"; package product; service ProductCatalog { rpc GetProductDetails (ProductDetailsRequest) returns (ProductDetailsReply); } message ProductDetailsRequest { int32 id = 1; } message ProductDetailsReply { int32 id = 1; string name = 2; string sku = 3; Price price = 4; } message Price { float amount = 1; string currencyCode = 2; }
スニペット1: ProductCatalogサービスの定義

proto3の厳密な型指定とフィールドの順序付けにより、メッセージの逆シリアル化はJSONの解析よりも大幅に負担が少なくなります。

RESTとgRPCの比較

要約すると、RESTとgRPCを比較する際の最も重要なポイントは次のとおりです。

休みgRPC
クロスプラットフォームはいはい
メッセージフォーマットカスタムですが、通常はJSONまたはXML プロトコルバッファ
メッセージペイロードサイズ中/大小さな
処理の複雑さより高い(テキスト解析) 低い(明確に定義されたバイナリ構造)
ブラウザのサポートはい(ネイティブ) はい(gRPC-Web経由)

それほど厳密ではないコントラクトとペイロードへの頻繁な追加が予想される場合は、JSONとRESTが最適です。 契約がより静的なままである傾向があり、速度が最も重要である場合、gRPCは一般的に勝ちます。 私が取り組んできたほとんどのプロジェクトで、gRPCはRESTよりも軽量でパフォーマンスが高いことが証明されています。

gRPCサービスの実装

合理化されたプロジェクトを構築して、gRPCを採用することがいかに簡単かを探りましょう。

APIプロジェクトの作成

開始するには、Visual Studio 2022 Community Edition(VS)で.NET6プロジェクトを作成します。 ASP.NET Core gRPCサービステンプレートを選択し、プロジェクト( InventoryAPIを使用)とその中の最初のソリューションInventoryの両方に名前を付けます。

Visual Studio 2022内の[新しいプロジェクトの構成]ダイアログ。この画面では、[プロジェクト名]フィールドに「InventoryAPI」と入力し、[場所]フィールドに[C:\ MyInventoryService]を選択し、[ソリューション名]フィールドに「Inventory」と入力しました。 。 「ソリューションとプロジェクトを同じディレクトリに配置する」のチェックを外したままにしました。

それでは、を選択しましょう。 フレームワークのNET6.0(長期サポート)オプション:

Visual Studio 2022内の追加情報ダイアログ。この画面では、[フレームワーク]ドロップダウンから[.NET 6.0(長期サポート)]を選択しました。 「Dockerを有効にする」のチェックを外したままにしました。

製品サービスの定義

プロジェクトを作成したので、VSはGreeterという名前のサンプルgRPCプロトタイプ定義サービスを表示します。 ニーズに合わせてGreeterのコアファイルを再利用します。

  • コントラクトを作成するために、 greet.protoの内容をSnippet 1に置き換え、ファイルの名前をproduct.protoに変更します。
  • サービスを作成するために、 GreeterService.csファイルの内容をSnippet 2に置き換え、ファイルの名前をProductCatalogService.csに変更します。
 using Grpc.Core; using Product; namespace InventoryAPI.Services { public class ProductCatalogService : ProductCatalog.ProductCatalogBase { public override Task<ProductDetailsReply> GetProductDetails( ProductDetailsRequest request, ServerCallContext context) { return Task.FromResult(new ProductDetailsReply { Id = request.Id, Name = "Purple Bowtie", Sku = "purbow", Price = new Price { Amount = 100, CurrencyCode = "USD" } }); } } }
スニペット2: ProductCatalogService

サービスはハードコードされた製品を返すようになりました。 サービスを機能させるには、 Program.csのサービス登録を変更して、新しいサービス名を参照するだけです。 この場合、 app.MapGrpcService<GreeterService>();名前を変更します。 app.MapGrpcService<ProductCatalogService>(); 新しいAPIを実行可能にします。

公正な警告:標準のプロトコルテストではありません

試してみたいと思うかもしれませんが、エンドポイントを対象としたブラウザーを介してgRPCサービスをテストすることはできません。 これを試みると、gRPCエンドポイントとの通信はgRPCクライアントを介して行われる必要があることを示すエラーメッセージが表示されます。

クライアントの作成

サービスをテストするために、VSの基本的なコンソールアプリテンプレートを使用して、APIを呼び出すgRPCクライアントを作成しましょう。 私はInventoryAppという名前を付けました。

便宜上、契約を共有するための相対ファイルパスを参照しましょう。 参照を手動で.csprojファイルに追加します。 次に、パスを更新し、 Clientモードを設定します。 注:相対参照を使用する前に、ローカルフォルダー構造に精通し、自信を持っておくことをお勧めします。

サービスプロジェクトファイルとクライアントプロジェクトファイルの両方に表示される.protoリファレンスは次のとおりです。

奉仕プロジェクトファイル
(クライアントプロジェクトファイルにコピーするコード)
クライアントプロジェクトファイル
(貼り付け編集後)
 <ItemGroup> <Content Update="Protos\product.proto" GrpcServices="Server" /> </ItemGroup>
 <ItemGroup> <Protobuf Include="..\InventoryAPI\Protos\product.proto" GrpcServices="Client" /> </ItemGroup>

ここで、サービスを呼び出すために、 Program.csのコンテンツを置き換えます。 私たちのコードはいくつかの目的を達成します:

  1. サービスエンドポイントの場所を表すチャネルを作成します(ポートは異なる場合があるため、実際の値については、 launchsettings.jsonファイルを参照してください)。
  2. クライアントオブジェクトを作成します。
  3. 簡単なリクエストを作成します。
  4. リクエストを送信します。
 using System.Text.Json; using Grpc.Net.Client; using Product; var channel = GrpcChannel.ForAddress("https://localhost:7200"); var client = new ProductCatalog.ProductCatalogClient(channel); var request = new ProductDetailsRequest { Id = 1 }; var response = await client.GetProductDetailsAsync(request); Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true })); Console.ReadKey();
スニペット3:新しいProgram.cs

ローンチの準備

コードをテストするには、VSでソリューションを右クリックし、[スタートアッププロジェクトの設定]を選択します。 [ソリューションプロパティページ]ダイアログで、次のことを行います。

  • [複数のスタートアッププロジェクト]の横にあるラジオボタンを選択し、[アクション]ドロップダウンメニューで、両方のプロジェクトInventoryAPIInventoryApp )を[開始]に設定します。
  • [ OK]をクリックします。

これで、VSツールバーの[開始]をクリックして(またはF5キーを押して)ソリューションを開始できます。 2つの新しいコンソールウィンドウが表示されます。1つはサービスがリッスンしていることを示し、もう1つは取得した製品の詳細を示します。

gRPC契約共有

次に、別の方法を使用して、gRPCクライアントをサービスの定義に接続しましょう。 最もクライアントがアクセス可能な契約共有ソリューションは、URLを介して定義を利用できるようにすることです。 他のオプションは、非常に脆弱(パスを介してファイルを共有)するか、より多くの労力を必要とします(ネイティブパッケージを介して契約を共有)。 URLを介した共有(SOAPおよびSwagger / OpenAPIのように)は柔軟性があり、必要なコードが少なくて済みます。

開始するには、 .protoファイルを静的コンテンツとして利用できるようにします。 ビルドアクションのUIが「ProtobufCompiler」に設定されているため、コードを手動で更新します。 この変更により、コンパイラーは.protoファイルをコピーして、Webアドレスから提供できるようになります。 この設定がVSUIを介して変更された場合、ビルドは壊れます。 次に、最初のステップは、 InventoryAPI.csprojファイルにSnippet4を追加することです。

 <ItemGroup> <Content Update="Protos\product.proto"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> <ItemGroup> <Content Include="Protos\product.proto" CopyToPublishDirectory="PreserveNewest" /> </ItemGroup>
スニペット4: InventoryAPIサービスプロジェクトファイルに追加するコード

次に、 ProductCatalogService.csファイルの先頭にあるSnippet 5にコードを挿入して、 .protoファイルを返すエンドポイントを設定します。

 using System.Net.Mime; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders;
スニペット5: Namespaceのインポート

そして今、 app.Run()の直前にSnippet6をProductCatalogService.csファイルに追加します。

 var provider = new FileExtensionContentTypeProvider(); provider.Mappings.Clear(); provider.Mappings[".proto"] = MediaTypeNames.Text.Plain; app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Protos")), RequestPath = "/proto", ContentTypeProvider = provider }); app.UseRouting();
Snippet 6:APIを介して.protoファイルにアクセスできるようにするコード

スニペット4〜6を追加すると、 .protoファイルの内容がブラウザに表示されます。

新しいテストクライアント

次に、VSの依存関係ウィザードを使用して既存のサーバーに接続する新しいコンソールクライアントを作成します。 問題は、このウィザードがHTTP/2を話さないことです。 したがって、HTTP / 1を介して通信し、サーバーを起動するようにサーバーを調整する必要があります。 サーバーが.protoファイルを利用できるようになったので、gRPCウィザードを介してサーバーにフックする新しいテストクライアントを構築できます。

  1. HTTP / 1を介して通信するようにサーバーを変更するには、 appsettings.jsonファイルを編集します。
    1. Protocolフィールド(パスKestrel.EndpointDefaults.Protocolsにあります)を調整して、 Httpsを読み取ります。
    2. ファイルを保存します。
  2. 新しいクライアントがこのproto情報を読み取るには、サーバーが実行されている必要があります。 当初は、VSの[スタートアッププロジェクトの設定]ダイアログから以前のクライアントとサーバーの両方を起動していました。 サーバーソリューションを調整して、サーバープロジェクトのみを開始してから、ソリューションを開始します。 (HTTPバージョンを変更したため、古いクライアントはサーバーと通信できなくなりました。)
  3. 次に、新しいテストクライアントを作成します。 VSの別のインスタンスを起動します。 「 APIプロジェクトの作成」セクションで説明されている手順を繰り返しますが、今回はコンソールアプリテンプレートを選択します。 プロジェクトとソリューションにInventoryAppConnectedという名前を付けます。
  4. クライアントシャーシを作成したら、gRPCサーバーに接続します。 VSSolutionExplorerで新しいプロジェクトを展開します。
    1. [依存関係]を右クリックし、コンテキストメニューで[接続されたサービスの管理]を選択します。
    2. [接続されたサービス]タブで、[サービス参照の追加]をクリックし、[ gRPC ]を選択します。
    3. [サービス参照の追加]ダイアログで、 URLオプションを選択し、サービスアドレスのhttpバージョンを入力します( launchsettings.jsonからランダムに生成されたポート番号を取得することを忘れないでください)
    4. [完了]をクリックして、簡単に保守できるサービス参照を追加します。

この例のサンプルコードに対して、自由に作業を確認してください。 内部的には、VSは最初のテストで使用したものと同じクライアントを生成したため、前のサービスのProgram.csファイルの内容をそのまま再利用できます。

コントラクトを変更するときは、更新された.proto定義に一致するようにクライアントのgRPC定義を変更する必要があります。 そのためには、VSの接続サービスにアクセスし、関連するサービスエントリを更新するだけで済みます。 これでgRPCプロジェクトが完了し、サービスとクライアントの同期を簡単に保つことができます。

あなたの次のプロジェクト候補:gRPC

私たちのgRPC実装は、gRPCを使用する利点を直接垣間見ることができます。 RESTとgRPCにはそれぞれ、契約の種類に応じて独自の理想的なユースケースがあります。 ただし、両方のオプションが当てはまる場合は、gRPCを試してみることをお勧めします。これにより、APIの将来の時代を先取りすることができます。