オリジナル関連
原文作者:Eduard Los
原文地址:https://medium.com/@eddyf1xxxer/bi-directional-streaming-and-introduction-to-grpc-on-asp-net-core-3-0-part-2-d9127a58dcdb
Demo地址:https://github.com/f1xxxer/CustomerGrpc
それでは、コードを見てみましょう。Visual Studio UIまたはコマンドラインコマンドを使用して、gRPCサービスプロジェクトを簡単に作成できます。dotnet new grpc -n YourGrpcService
私のソリューションでは、gRPCサーバーとクライアントコードの両方がC#に含まれています。gRPCサーバーはクライアント接続を管理してメッセージを処理し、接続されているすべてのクライアントにメッセージをブロードキャストしています。クライアントは、クライアントから入力を受け取り、それをサーバーに送信し、他のクライアントからサーバーから送信されたメッセージも受け入れます。
私たちは、最初のサーバーコードCustomerGrpcプロジェクトを参照してください、しかし、その前に、私は、標準だと思いStartup.csとのProgram.csファイルの内容の一部からポイントを。
public class Startup {
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices (IServiceCollection services) {
services.AddGrpc (opts => {
opts.EnableDetailedErrors = true;
opts.MaxReceiveMessageSize = 4096;
opts.MaxSendMessageSize = 4096;
});
services.AddTransient<IChatRoomService, ChatRoomService> ();
services.AddSingleton<IChatRoomProvider, ChatRoomProvider> ();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure (IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment ()) {
app.UseDeveloperExceptionPage ();
}
app.UseRouting ();
app.UseEndpoints (endpoints => {
endpoints.MapGrpcService<Services.CustomerService> ();
endpoints.MapGet ("/",
async context => {
await context.Response.WriteAsync (
"Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
}
}
services.AdddGrpc() ;メソッドはgRPCを可能にしました。このメソッドは、gRPC呼び出しを処理するためのパイプを構築するために使用されるさまざまなサービスとミドルウェアを追加します。Undercoverメソッドは、GrpcMarkerServiceクラスを使用して、必要なすべてのgRPCサービスが確実に追加されるようにし、基本的なHTTP / 2メッセージで動作するミドルウェアもいくつか追加されます。後でパイプラインで使用されるGrpcServiceOptionsタイプを介してサービス構成を提供することもできます。たとえば、最大受信メッセージサイズは次のように構成できます。
services.AddGrpc(opts =>
{
opts.EnableDetailedErrors = true;
opts.MaxReceiveMessageSize = 4096;
opts.MaxSendMessageSize = 4096;
});
私たちにとって、エンドポイントルーティングにはもう1つの興味深いメソッドがあります:endpoints.MapGrpcService <Services.CustomerService>();このメソッドは、gRPCサービスをルーティングパイプラインに追加します。ASP.NET Coreでは、ルーティングパイプラインはミドルウェアと関数の間で共有されます。つまり、MVCコントローラーなどの他のリクエストハンドラーを含めることができます。他のリクエストハンドラは、構成されたgRPCサービスと並行して動作します。以下を参照してください:
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<Services.CustomerService>();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync(
"Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
次に、Kestrelサーバーを構成する必要があります。
ケストレルgRPCエンドポイント:
- HTTP / 2が必要です。
- 保護にはHTTPSを使用する必要があります。
HTTP / 2
gRPCはHTTP / 2を必要とし、ASP.NET CoreのgRPCはHttpRequest.ProtocolがHTTP / 2であることを確認します。
Kestrelは、最新のほとんどのオペレーティングシステムでHttp / 2をサポートしています。デフォルトでは、KestrelエンドポイントはHTTP / 1.1およびHTTP / 2接続をサポートするように構成されています。
gRPCのHTTPS KestrelエンドポイントはHTTPSを使用して保護する必要があります。開発中、https:// localhost:5001は、ASP.NET Core開発証明書がある場合、HTTPSエンドポイントを自動的に作成します。設定は必要ありません。
本番環境では、HTTPSを明示的に構成する必要があります。この場合、次のようになります。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
// Setup a HTTP/2 endpoint without TLS for OSX.
options.ListenLocalhost(5001, o => o.Protocols = HttpProtocols.Http2);
});
webBuilder.UseStartup<Startup>();
});
}
macOSがトランスポート層セキュリティ(Http / 2.)を使用したASP.NET Core gRPC をサポートしないという既知の問題があることに注意してください。macOSでgRPCサービスを正常に実行するには、追加の構成が必要です。これはまさに私たちの状況です。そのため、オプションでTLSなしのHTTP / 2行を使用してTLSを無効にする必要があります.ListenLocalhost(5001、o => o.Protocols = HttpProtocols.Http2)アプリケーション開発中に使用されます。本番アプリケーションでは、常に伝送セキュリティを使用する必要があります。詳細については、「ASP.NET CoreのgRPCのセキュリティに関する考慮事項」を参照してください。
それでは、gRPCカスタマーサービスを見てみましょう。最初は、私たちが見る、私たちですたCustomerServiceクラスからCustomerGrpc.CustomerService.CustomerServiceBaseのクラス継承。
このクラスは.protoファイルに基づいて生成されます。ファイルのF12キーを押すと、サービス、メッセージ、および通信を説明する自動生成されたコードの束が表示されます。
このクラスでは、親クラスで定義されている2つのカスタムメソッド、JoinCustomerChatとSendMessageToChatRoomをオーバーライドします。これが基本的にすべてのコードです。JoinCustomerChatは比較的単純な要求/応答モデルを示しています。このモデルでは、JoinCustomerRequestの顧客情報を送信し、顧客がJoinCustomerReplyに参加するチャットルームのRoomIdでそれを受信します。
public override async Task<JoinCustomerReply> JoinCustomerChat (JoinCustomerRequest request, ServerCallContext context)
{
return new JoinCustomerReply { RoomId = await _chatRoomService.AddCustomerToChatRoomAsync (request.Customer) };
}
次に、より興味深いSendMessageToChatRoomメソッドがあります。これは、双方向ストリーミングのデモです。
public override async Task SendMessageToChatRoom (IAsyncStreamReader<ChatMessage> requestStream, IServerStreamWriter<ChatMessage> responseStream, ServerCallContext context)
{
var httpContext = context.GetHttpContext ();
_logger.LogInformation ($"Connection id: {httpContext.Connection.Id}");
if (!await requestStream.MoveNext ()) {
return;
}
_chatRoomService.ConnectCustomerToChatRoom (requestStream.Current.RoomId, Guid.Parse (requestStream.Current.CustomerId), responseStream);
var user = requestStream.Current.CustomerName;
_logger.LogInformation ($"{user} connected");
try {
while (await requestStream.MoveNext ())
{
if (!string.IsNullOrEmpty (requestStream.Current.Message))
{
if (string.Equals (requestStream.Current.Message, "qw!", StringComparison.OrdinalIgnoreCase)) {
break;
}
await _chatRoomService.BroadcastMessageAsync (requestStream.Current);
}
}
}
catch (IOException)
{
_chatRoomService.DisconnectCustomer (requestStream.Current.RoomId, Guid.Parse (requestStream.Current.CustomerId));
_logger.LogInformation ($"Connection for {user} was aborted.");
}
}
メソッドのパラメーターを詳しく見てみましょう。IAsyncStreamReader
IServerStreamWriter
ServerCallContextコンテキストは、呼び出しのHttpContextやHostNameなど、いくつかの便利な属性を提供します。
このメソッドのロジックは複雑ではありません。サーバーで呼び出しが受信されると、最初にIServerStreamWriterを格納します
_chatRoomService.ConnectCustomerToChatRoom(requestStream.Current.RoomId, Guid.Parse(requestStream.Current.CustomerId), responseStream);
次に、しばらくの間(requestStream.MoveNext()を待機)ループで、クライアントから受信したメッセージを参照し、現在接続されている別のクライアントにブロードキャストします。
await _chatRoomService.BroadcastMessageAsync(requestStream.Current);
BroadcastMessageAsync()メソッドのコードは、接続されているすべてのクライアントの応答ストリームをトラバースし、ストリームにメッセージを書き込みます。
foreach (var customer in chatRoom.CustomersInRoom)
{
await customer.Stream.WriteAsync(message);
Console.WriteLine($"Sent message from {message.CustomerName} to {customer.Name}");
}
また、接続の失敗を処理し、対応するストリームをチャットルームから削除するためのtry / catchブロックもあります。
catch (IOException)
{
_chatRoomService.DisconnectCustomer(requestStream.Current.RoomId, Guid.Parse(requestStream.Current.CustomerId));
_logger.LogInformation($"Connection for {user} was aborted.");
}
これは基本的に、gRPCに直接関連するすべてのサーバーロジックです。ここでは、サービスとラッパーにいくつかの追加機能を作成しました。
これで、クライアントコードを簡単に表示できます。まず、GrpcChannelを作成し、それをgRPCクライアントに提供します。
var channel = GrpcChannel.ForAddress("http://localhost:5001", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new CustomerService.CustomerServiceClient(channel);
そして、クライアントJoinCustomerChatAsyncのリターンは、チャットルームをRoomId。その後、SendMessageToChatRoom呼び出しを実行してメッセージ交換を開始することにより、双方向フローを開きます。
using (var streaming = client.SendMessageToChatRoom (new Metadata { new Metadata.Entry ("CustomerName", customer.Name) }))
{
var response = Task.Run (async () =>
{
while (await streaming.ResponseStream.MoveNext ())
{
Console.ForegroundColor = Enum.Parse<ConsoleColor> (streaming.ResponseStream.Current.Color);
Console.WriteLine ($"{streaming.ResponseStream.Current.CustomerName}: {streaming.ResponseStream.Current.Message}");
Console.ForegroundColor = Enum.Parse<ConsoleColor> (customer.ColorInConsole);
}
});
...
while (!string.Equals (line, "qw!", StringComparison.OrdinalIgnoreCase)) {
await streaming.RequestStream.WriteAsync (new ChatMessage {
Color = customer.ColorInConsole,
CustomerId = customer.Id,
CustomerName = customer.Name,
Message = line,
RoomId = joinCustomerReply.RoomId
});
line = Console.ReadLine ();
DeletePrevConsoleLine ();
}
await streaming.RequestStream.CompleteAsync ();
}
接続を開いた後、バックグラウンドタスクでループを開始します。このループは、応答ストリームからメッセージを読み取り、コンソールに表示します。2番目のループは、単にキーボードから入力を受け取り、その入力をサーバーに送信します。
全体的に、コードはそれほど複雑ではありませんが、.netコアとc#を使用して独自のgRPCサービスを実装するために使用または選択を設計する必要がある場所を示します。
著者Eduard Losに心から感謝します。私たちと共有してください。
気に入ったら、きっと気に入ってもらえると思います。