.NET 客户端调用gRPC

通过 .NET 客户端调用 gRPC 服务

.NET gRPC 客户端库在gRPC .net. client NuGet 包中提供。 本文档介绍如何执行以下操作:

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

配置 gRPC 客户端

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

从通道创建 gRPC 客户端。 开始使用 GrpcChannel.ForAddress 创建通道,然后使用通道创建 gRPC 客户端:

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

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

var channel = GrpcChannel.ForAddress("https://localhost:5001");

var greeterClient = new Greet.GreeterClient(channel);
var counterClient = new Count.CounterClient(channel);

// Use clients to call gRPC services

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

  • 创建通道是一种开销高昂的操作。 为 gRPC 调用重用通道可带来性能优势。
  • 将通过通道创建 gRPC 客户端。 gRPC 客户端是轻型对象,无需缓存或重复使用。
  • 可以从通道(包括不同类型的客户端)创建多个 gRPC 客户端。
  • 通道和从通道创建的客户端可以安全地由多个线程使用。
  • 从通道创建的客户端可以同时进行多次调用。

GrpcChannel.ForAddress 不是创建 gRPC 客户端的唯一选项。 如果要从 ASP.NET Core 的应用程序中调用 gRPC 服务,请考虑gRPC 客户端工厂集成 gRPC 与 HttpClientFactory 的集成提供了创建 gRPC 客户端的集中式替代方法。

备注

需要额外的配置才能通过 .net 客户端调用不安全的 gRPC 服务

进行 gRPC 调用

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

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

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

一元调用

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

var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });

Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World

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

  • GreeterClient.SayHelloAsync-异步调用 Greeter.SayHello 服务。 可以等待。
  • GreeterClient.SayHello-调用 Greeter.SayHello 服务,直到完成。 不要在异步代码中使用。

服务器流式处理调用

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

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

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

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

客户端流式处理调用

客户端流式处理调用在客户端发送消息的情况下启动。 客户端可以选择通过 RequestStream.WriteAsync发送消息。 当客户端已经完成发送消息 RequestStream.CompleteAsync 应调用来通知服务。 当服务返回响应消息时,调用完成。

var client = new Counter.CounterClient(channel);
using (var call = client.AccumulateCount())
{
    for (var i = 0; i < 3; i++)
    {
        await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 });
    }
    await call.RequestStream.CompleteAsync();

    var response = await call;
    Console.WriteLine($"Count: {response.Count}");
    // Count: 3
}

双向流式处理调用

双向流式处理调用在客户端发送消息的情况下启动。 客户端可以选择通过 RequestStream.WriteAsync发送消息。 通过 ResponseStream.MoveNext()ResponseStream.ReadAllAsync()可以访问从服务流式处理的消息。 ResponseStream 没有更多消息时,双向流式处理调用完成。

using (var call = client.Echo())
{
    Console.WriteLine("Starting background task to receive messages");
    var readTask = Task.Run(async () =>
    {
        await foreach (var response in call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine(response.Message);
            // Echo messages sent to the service
        }
    });

    Console.WriteLine("Starting to send messages");
    Console.WriteLine("Type a message to echo then press enter.");
    while (true)
    {
        var result = Console.ReadLine();
        if (string.IsNullOrEmpty(result))
        {
            break;
        }

        await call.RequestStream.WriteAsync(new EchoMessage { Message = result });
    }

    Console.WriteLine("Disconnecting");
    await call.RequestStream.CompleteAsync();
    await readTask;
}

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

其他资源