SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

.NET最新版でASP.NET Core

ASP.NET CoreによるgRPCクライアントの開発を理解しよう

.NET最新版でASP.NET Core 第11回

  • X ポスト
  • このエントリーをはてなブックマークに追加

gRPCサーバをサーバサイドストリーミング対応に拡張する

 前回では、Unary方式の手続きを実装するgRPCサーバアプリケーションを作成しました。これに、サーバサイドストリーミング方式の手続きを追加します。SayHello手続きの拡張版として、サーバサイドストリーミングにてメッセージを複数返してくるPlentySayHello手続きを実装してみます。

プロトコル定義ファイルへの手続きの追加

 プロトコル定義ファイルProto/greet.protoにおいて、GreeterサービスにPlentySayHello手続きを追加し、リクエストのためのメッセージPlentyHelloRequestを追加します。個々のレスポンスの形式は変わらないので、HelloReplyメッセージはSayHello手続きと共通とします。

GrpcServerSample/Protos/greet.proto
…略…
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
  // 複数回対応のSayHello手続き
  rpc PlentySayHello (PlentyHelloRequest) returns (stream HelloReply);	(1)
}
…略…
// 複数回対応のリクエストメッセージ
message PlentyHelloRequest {	(2)
  string name = 1;
  int32 count = 2;
}

 (1)が、追加したPlentySayHello手続きです。SayHello手続きとほとんど同じように見えますが、戻り値にstreamキーワードが付いている点に注目してください。これは、手続きがサーバサイドストリーミング方式に対応することを示すキーワードです。なお、クライアントサイドストリーミングでは引数の方にstreamを指定します。

 (2)は、PlentySayHello手続きの引数であるPlentyHelloRequestメッセージの定義です。HelloRequestメッセージと異なるのは、int32型のcountフィールドを追加してレスポンスの回数を指定している点です。サーバサイドストリーミングにより、指定した回数だけサーバからレスポンスHelloReplyが返るようになります。

サービスへの手続きの追加

 ここでdotnet buildコマンドを実行すると、修正されたプロトコル定義ファイルに基づきスタブファイルが再作成されます。この時点で、Services/GreeterService.csファイルにPlentySayHelloメソッドを実装できます。スタブに追加されたPlentySayHelloメソッドの定義に基づき、Services/GreeterService.csファイルにメソッドを実装してみます。

GrpcServerSample/Services/GreeterService.cs
…略…
public override async Task PlentySayHello(PlentyHelloRequest request,
    IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)	(1)
{
    for (var count = 1; count <= request.Count; count++)
    {
        var reply = new HelloReply { Message = $"({count})Hello " + request.Name };
        await responseStream.WriteAsync(reply);		(2)
    }
    return;
}
…略…

 (1)の通り、サーバサイドストリーミングに対応したPlentySayHelloメソッドのシグネチャは、Unaryに対応するSayHelloメソッドと異なっています。SayHelloメソッドがHelloRequest型とServerCallContext型を引数に受け取ってTask<HelloReply>型を返すのに対して、PlentySayHelloメソッドは第2引数にIServerStreamWriter<HelloReply>型が追加となっており、戻り値は単なるTask型となります。

 IServerStreamWriterは、その名の通りストリームの書き込みのためのインタフェースで、メソッドの戻り値ではなくこのインスタンスを使って非同期にレスポンスを返します。このため、メソッドの戻り値は特に必要ないのです。(2)がその処理であり、生成したHelloReplyオブジェクトをWriteAsyncメソッドによりストリームに書き込んでいます。

[NOTE]Task型

 Task型は、非同期メソッドの戻り値の型であり、これを戻り値とするメソッドはawait演算子によって実行の終了を待機することができます(単にvoid型を戻す場合は、待機できません)。非同期メソッドが値を返したい場合には、戻り値をTask<TResult>(TResultは値の型)とします。Unary方式の手続きではメソッドが値を返したいのでTask<HelloReply>型を戻り値としていたのに対し、サーバサイドストリーミングの手続きではメソッドは特に値を返す必要はないので、単なるTaskを戻り値としています。後述の、サーバサイドストリーミング対応のクライアントのコードは、これを踏まえて読んでください。

 ここでサーバを起動し、grpcurlで動作を確認して、以下のように出力されれば実装は成功です。

% grpcurl -plaintext -proto Protos/greet.proto -d '{"name": "Nao", "count": 5}' localhost:5046 greet.Greeter/PlentySayHello 
{
  "message": "(1)Hello Nao"
}
…略(番号を変えて計5回表示される)…

サーバサイドストリーミング対応に合わせたクライアントの拡張

 gRPCサーバをサーバサイドストリーミング対応に拡張したので、それに合わせてgRPCクライアントも拡張してみます。

プロトコル定義ファイルの修正

 上記で拡張したサーバアプリケーションのプロトコル定義ファイルProtos/greet.protoの修正を、クライアントアプリケーションのProtos/greet.protoファイルにも反映します。改めてコピーして、名前空間だけ修正しても構いません。

クライアントコードの追加

 サーバサイドストリーミングに対応したクライアントのコードを追加します。このクライアントは、第1引数にあいさつ相手の名前、第2引数が指定されていればあいさつの回数として受け取り、回数が1回ならUnary、2回以上ならサーバサイドストリーミングで手続きを呼び出して結果を表示します。

GrpcClientSample/Program.cs
using System.Threading.Tasks;		(1)
using Grpc.Core;
…略…

// 引数の取得
if (args.Length < 1)		(2)
{
    Console.WriteLine("引数が1個以上必要です");
    return;
}
var name = args[0];
var count = 1;
if (args.Length > 1)
{
    Int32.TryParse(args[1], out count);
    if (count < 1)
    {
        Console.WriteLine("回数は1以上にしてください");
        return;
    }
}

// チャネルとクライアントの生成
using var channel = GrpcChannel.ForAddress($"http://localhost:5046");	(3)
var client = new Greeter.GreeterClient(channel);
if (count == 1)
{
    // Unary。SayHelloAsyncメソッドを非同期で呼び出し結果を表示
    …Unaryと同じなので略…
}
else
{
    // サーバサイドストリーミング対応。PlentySayHelloメソッドを呼び出し、
    // 結果を非同期で受け取って順番に表示
    using var response = client.PlentySayHello(
        new PlentyHelloRequest { Name = name, Count = count});		(4)
    await foreach (var reply in response.ResponseStream.ReadAllAsync())	(5)
    {
        Console.WriteLine(reply.Message);
    }
}

 (1)は、必要な名前空間のインポートです。特に、Grpc.Coreはサーバサイドストリーミングで必要になってくるので、スタブコードで指定されているものを漏らさず指定する必要があります。

 (2)は、引数の処理です。あいさつ相手の名前はnameに、回数はcountにセットします。条件を満たさない場合にはメッセージを表示してプログラムを終了します。

 (3)は、既述の通りチャネルの生成とクライアントの生成です。

 (4)は、サーバサイドストリーミングの場合の手続き呼び出しです。PlentySayHelloメソッドの戻り値は、あくまでも手続きの戻り値(Task型)を受け取るためのものであることに注目してください(上記の[NOTE]参照)。続く非同期foreach文にて、ReadAsyncAllメソッドを呼び出して順番にレスポンスを受け取り、表示しています。

 この時点で、dotnet runコマンドを実行し、以下のように実行結果を確認できればクライアントの作成は成功です。

% dotnet run Nao 5
(1)Hello Nao
…略(番号を変えて計5回表示される)…

まとめ

 前回と今回は、ASP.NET CoreにおけるgRPCのサポートについて見てきました。次回は、リアルタイム通信のフレームワークであるSignalRを紹介します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
.NET最新版でASP.NET Core連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook <個人紹介> WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18363 2023/10/11 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング