玩转gRPC和.NET 6:客户端
在.NET中创建一个gRPC客户端。
在我之前的文章中,我着重介绍了如何使用.NET创建一个gRPC服务器。现在,我想告诉你我们如何使用.NET创建一个gRPC客户端。
在下一张图片中,我标记了本教程的重点项目。
前提条件
我使用的是macOS,接下来的命令是针对这个操作系统的。
- gRPC编译器。要安装Protobuf编译器,你可以在终端中执行这个。
brew install protobuf
brew install grpcurl
- grpcui:建立在gRPCurl之上,为gRPC增加了一个交互式的Web UI,类似于Postman和Swagger UI等工具。
brew install grpcui
操作步骤
在一般情况下,我们应该遵循以下步骤。
- 创建一个项目。
- 在项目中添加gRPC的依赖项。
- 在你的项目中添加proto文件。
- 注册创建的proto文件并编译项目。
- 实现业务逻辑。
在这个例子中,我们将使用一个名为CountryGrpcClient 的.NET项目,调用服务器来搜索、创建或获得一个国家的列表。 CountryGrpc.proto 文件(如下所述)声明了远程程序。
1. 创建一个项目
要创建一个gRPC模板.NET项目,请打开一个终端,执行下一个命令。
dotnet new console -o grpc.country.client -n CountryGrpcClient
输出结果是这样的。
这里。
-o参数用于定义项目的目录名称: 。grpc.country.client-n参数用于定义项目名称: 。CountryGrpcClient
2. 将gRPC依赖项添加到项目中
dotnet add CountryGrpcClient.csproj package Grpc.Net.Client --version '2.47.0'
dotnet add CountryGrpcClient.csproj package Grpc.Tools --version '2.47.0'
dotnet add CountryGrpcClient.csproj package Google.Protobuf --version '3.21.5'
你可以看到,你的CountryGrpcClient.csproj 中的条目ItemGroup 已经变成了这样的内容。
3. 在项目中添加原型文件
在你的项目目录中,创建一个名为Protos 的文件夹,里面有一个文件CountryGrpc.protos 。
mkdir ./Protos
touch ./Protos/CountryGrpc.proto
在这一步,我将使用在上一篇文章中创建的同样的proto文件,在这篇文章中我们创建了一个gRPC服务器。
在之前创建的CountryGrpc.proto 文件中复制接下来的几行。
ProtoBuf
syntax = "proto3";
/*The Proto file that has Empty message definition*/
import "google/protobuf/empty.proto";
// Defining the namespace in which the generate classes will be
option csharp_namespace = "Sumaris.Grpc.Services";
// The service name will be used by the compiler when generate the base classes
// Here I declare five procedure
service CountryService{
//Server streaming RPC
rpc getAllCountries(google.protobuf.Empty)
returns (stream Country);
// Unitary RPC
rpc listAllCountries(google.protobuf.Empty)
returns ( CountryList);
// Unitary RPC
rpc findCountryByName( FindCountryByNameRequest )
returns (FindCountryByNameResponse);
// Unitary RPC
rpc createCountry (CountryCreateRequest)
returns (CountryCreateRespopnse);
// Bidrectional streaming RPC
rpc findCountriesByNames( stream FindCountryByNameRequest)
returns (stream Country);
}
message Country{
string name=1;
string capitalCity=2;
float area=3;
}
message CountryList{repeated Country country = 1;}
message FindCountryByNameRequest{string name=1;}
message FindCountryByNameResponse{Country country=1;}
message CountryCreateRequest{ Country country=1;}
message CountryCreateRespopnse{bool created=1;}
4. 注册创建的Proto文件并编译项目
在你的配置项目文件中添加以下几行,CountryGrpcClient.csproj 。
XML
<ItemGroup>
<Protobuf Include="./Protos/CountryGrpc.proto" GrpcServices="Client" />
</ItemGroup>
打开终端,进入项目,并执行下一条命令来编译项目。
dotnet build
构建过程会创建两个文件,CountryGrpc.cs 和CountryGrpcGrpc.cs , 在 obj/Debug/net6.0/Protos 路径中。 文件CountryGrpcGrpc.cs 包含我们将用作客户端与gRPC服务器交互的类。
5.实现业务逻辑
打开IDE,编辑Program.cs 文件,添加调用服务器的代码。
C#
using System.Threading.Tasks;
using Grpc.Net.Client;
using Sumaris.Grpc.Services;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
var countryNames = new string[] { "Perú", "Ecuador", "Chile", "Brasil", "Argentina", "Venezuela" };
// See https://aka.ms/new-console-template for more information
using var channel =
GrpcChannel.ForAddress("http://localhost:5000");
CountryService.CountryServiceClient? client =
new CountryService.CountryServiceClient(channel);
// Consume unitary
// rpc listAllCountries(google.protobuf.Empty) returns ( CountryList);
var reply = await client.listAllCountriesAsync(new Empty());
Console.WriteLine("Hello, World!");
Console.WriteLine("Greeting: " + reply.Country);
// Consume stream server
//rpc getAllCountries(google.protobuf.Empty) returns (stream Country);
printServerStream();
// Consume unitary
//rpc findCountryByName(FindCountryByNameRequest ) returns(FindCountryByNameResponse);
// Consume client streaming server streaming
//rpc findCountriesByNames( stream FindCountryByNameRequest) returns (stream Country);
var asyncDuplexStreamingCall = client.findCountriesByNames();
callStreamFromClient(asyncDuplexStreamingCall.RequestStream);
getStreamFromServer(asyncDuplexStreamingCall.ResponseStream);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
async void printServerStream()
{
var reply2 = client.getAllCountries(new Empty()).ResponseStream;
Console.WriteLine("Greeting2: ");
while (await reply2.MoveNext())
{
Console.Write(reply2.Current);
}
Console.WriteLine();
}
async void callStreamFromClient(IClientStreamWriter<FindCountryByNameRequest> request)
{
foreach (var countryName in countryNames)
{
var d = new FindCountryByNameRequest();
d.Name = countryName;
await request.WriteAsync(d);
}
await request.CompleteAsync();
}
async void getStreamFromServer(IAsyncStreamReader<Country> response)
{
await foreach(var p in response.ReadAllAsync())
{
Console.Write($">>{response.Current}");
}
Console.WriteLine("Terminando ...");
}
在接下来的图片中,我想再解释一下你是如何消费远程程序的。
标记为1的步骤让你创建*"客户端*"对象。你只需要一个客户端来与服务器交互。
从这一点出发,你用*"客户端 "*来调用每一个远程过程,正如你在步骤2中看到的那样。在这种情况下,客户端正在调用一个名为getAllCountries 的服务器流式RPC。服务器以异步和流式的方式将数据发送到客户端(第3步)。客户端也以流式方式读取数据,直到服务器完成发送(第4步)。
现在我们看到了如何调用一个流式服务器gRPC,我将告诉你如何调用一个双向流式gRPC。
在这种情况下,我们使用之前创建的相同的*"客户端*"对象,即步骤1,并调用远程双向流媒体gRPC。在这个例子中,它是findCountriesByName ,它返回一个对象AsyncServerStreamingCall (步骤2)*。*这个对象包裹了两个对象:用于向服务器发送流媒体数据的RequestStream (步骤3),以及用于获取服务器返回的流媒体数据的ResponseStream (步骤5)。
服务器处理客户端在传输流中发送的每个传入对象,应用其业务逻辑,并异步地将其响应写到传输返回流中,正如我们在步骤4中看到的。客户端使用IAsyncStreamReader 读取服务器发送的传入响应,正如你在步骤5中看到的。
当客户端没有更多的数据要发送给服务器时,它必须通知服务器(步骤3中的红框),这样服务器就完成了异步读取请求的过程,可以在步骤4中离开foreach 。在这种情况下,服务器结束其进程,并通知客户端没有更多的数据需要读取。这时,客户端在步骤5中退出foreach 。
这样就完成了双向传输。
现在我已经向你展示了一个双向流式gRPC,你可以用它来实现一个客户端流式gRPC。
结论
我们使用协议缓冲区文件来生成.NET中的客户端实现,并使用async/await来管理流式服务器或客户端。 我们看到了如何调用一个流媒体服务器和双向流媒体gRPC。
如果你有任何问题或反馈,请随时告诉我。
谢谢你!