使用 .NET 客户端调用 gRPC 服务Call gRPC services with the .NET client

本文内容

Grpc.Net.Client NuGet 包提供了 .NET gRPC 客户端库。本文档介绍如何执行以下操作:

  • 配置 gRPC 客户端以调用 gRPC 服务。
  • 对一元、服务器流式处理、客户端流式处理和双向流式处理方法进行 gRPC 调用。

配置 gRPC 客户端Configure gRPC client

gRPC 客户端是从 *.proto 文件生成的具体客户端类型。具体 gRPC 客户端具有转换为 *.proto 文件中 gRPC 服务的方法。

gRPC 客户端是通过通道创建的。首先使用 GrpcChannel.ForAddress 创建一个通道,然后使用该通道创建 gRPC 客户端:

  1. var channel = GrpcChannel.ForAddress("https://localhost:5001");
  2. var client = new Greet.GreeterClient(channel);

通道表示与 gRPC 服务的长期连接。创建通道后,进行配置,使其具有与调用服务相关的选项。例如,可在 GrpcChannelOptions 上指定用于调用的 HttpClient、发收和接收消息的最大大小以及记录日志,并将其与 GrpcChannel.ForAddress 一起使用。有关选项的完整列表,请参阅客户端配置选项

  1. var channel = GrpcChannel.ForAddress("https://localhost:5001");
  2. var greeterClient = new Greet.GreeterClient(channel);
  3. var counterClient = new Count.CounterClient(channel);
  4. // Use clients to call gRPC services

通道及客户端性能和使用情况:

  • 创建通道成本高昂。重用 gRPC 调用的通道可提高性能。
  • gRPC 客户端是使用通道创建的。gRPC 客户端是轻型对象,无需缓存或重用。
  • 可从一个通道创建多个 gRPC 客户端(包括不同类型的客户端)。
  • 通道和从该通道创建的客户端可由多个线程安全使用。
  • 从通道创建的客户端可同时进行多个调用。

GrpcChannel.ForAddress 不是创建 gRPC 客户端的唯一选项。如果要从 ASP.NET Core 应用调用 gRPC 服务,请考虑 gRPC 客户端工厂集成gRPC 与 HttpClientFactory 集成是创建 gRPC 客户端的集中式操作备选方案。

备注

使用 .NET 客户端调用不安全的 gRPC 服务,需要其他配置。

备注

Xamarin 当前不支持通过 HTTP/2 使用 Grpc.Net.Client 调用 gRPC。我们正在改进 Xamarin 未来版本中的 HTTP/2 支持。Grpc.CoregRPC-Web 是立即生效的可行备选方案。

进行 gRPC 调用Make gRPC calls

在客户端上调用方法会启动 gRPC 调用。gRPC 客户端将处理消息序列化,并为 gRPC 调用寻址到正确服务。

gRPC 具有不同类型的方法。使用客户端进行 gRPC 调用的方式取决于所调用的方法类型。gRPC 方法类型如下:

  • 一元
  • 服务器流式处理
  • 客户端流式处理
  • 双向流式处理

一元调用Unary call

一元调用从客户端发送请求消息开始。服务结束后,返回响应消息。

  1. var client = new Greet.GreeterClient(channel);
  2. var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
  3. Console.WriteLine("Greeting: " + response.Message);
  4. // Greeting: Hello World

*.proto 文件中的每个一元服务方法将在用于调用方法的具体 gRPC 客户端类型上产生两个 .NET 方法:异步方法和阻塞方法 。例如,GreeterClient 具有两种调用 SayHello 的方法:

  • GreeterClient.SayHelloAsync - 以异步方式调用 Greeter.SayHello 服务。敬请期待。
  • GreeterClient.SayHello - 调用 Greeter.SayHello 服务并阻塞,直至结束。不要在异步代码中使用。

服务器流式处理调用Server streaming call

服务器流式处理调用从客户端发送请求消息开始。ResponseStream.MoveNext() 读取从服务流式处理的消息。ResponseStream.MoveNext() 返回 false 时,服务器流式处理调用已完成。

  1. var client = new Greet.GreeterClient(channel);
  2. using (var call = client.SayHellos(new HelloRequest { Name = "World" }))
  3. {
  4. while (await call.ResponseStream.MoveNext())
  5. {
  6. Console.WriteLine("Greeting: " + call.ResponseStream.Current.Message);
  7. // "Greeting: Hello World" is written multiple times
  8. }
  9. }

如果使用的是 C# 8 或更高版本,则可使用 await foreach 语法读取消息。IAsyncStreamReader<T>.ReadAllAsync() 扩展方法读取响应数据流中的所有消息:

  1. var client = new Greet.GreeterClient(channel);
  2. using (var call = client.SayHellos(new HelloRequest { Name = "World" }))
  3. {
  4. await foreach (var response in call.ResponseStream.ReadAllAsync())
  5. {
  6. Console.WriteLine("Greeting: " + response.Message);
  7. // "Greeting: Hello World" is written multiple times
  8. }
  9. }

客户端流式处理调用Client streaming call

客户端无需发送消息即可开始客户端流式处理调用 。客户端可选择使用 RequestStream.WriteAsync 发送消息。客户端发送完消息后,应调用 RequestStream.CompleteAsync 来通知服务。服务返回响应消息时,调用完成。

  1. var client = new Counter.CounterClient(channel);
  2. using (var call = client.AccumulateCount())
  3. {
  4. for (var i = 0; i < 3; i++)
  5. {
  6. await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 });
  7. }
  8. await call.RequestStream.CompleteAsync();
  9. var response = await call;
  10. Console.WriteLine($"Count: {response.Count}");
  11. // Count: 3
  12. }

双向流式处理调用Bi-directional streaming call

客户端无需发送消息即可开始双向流式处理调用 。客户端可选择使用 RequestStream.WriteAsync 发送消息。使用 ResponseStream.MoveNext()ResponseStream.ReadAllAsync() 可访问从服务流式处理的消息。ResponseStream 没有更多消息时,双向流式处理调用完成。

  1. using (var call = client.Echo())
  2. {
  3. Console.WriteLine("Starting background task to receive messages");
  4. var readTask = Task.Run(async () =>
  5. {
  6. await foreach (var response in call.ResponseStream.ReadAllAsync())
  7. {
  8. Console.WriteLine(response.Message);
  9. // Echo messages sent to the service
  10. }
  11. });
  12. Console.WriteLine("Starting to send messages");
  13. Console.WriteLine("Type a message to echo then press enter.");
  14. while (true)
  15. {
  16. var result = Console.ReadLine();
  17. if (string.IsNullOrEmpty(result))
  18. {
  19. break;
  20. }
  21. await call.RequestStream.WriteAsync(new EchoMessage { Message = result });
  22. }
  23. Console.WriteLine("Disconnecting");
  24. await call.RequestStream.CompleteAsync();
  25. await readTask;
  26. }

双向流式处理调用期间,客户端和服务可在任何时间互相发送消息。与双向调用交互的最佳客户端逻辑因服务逻辑而异。

其他资源Additional resources