Вызов служб gRPC с помощью клиента .NET
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 8 этой статьи.
Клиентская библиотека .NET gRPC доступна в пакете NuGet Grpc.Net.Client. В этом документе объясняется, как выполнять следующие задачи:
- Настройка клиента gRPC для вызова служб gRPC.
- Вызовы gRPC для унарного метода, методов потоковой передачи сервера, потоковой передачи клиента и двунаправленной потоковой передачи.
Настройка клиента gRPC
Клиенты gRPC — это конкретные типы клиентов, создаваемые в файлах .proto
. Конкретный клиент gRPC использует методы, которые выполняют преобразование для служб gRPC в файле .proto
. Например, служба с именем Greeter
создает тип GreeterClient
с методами для вызова службы.
Клиент gRPC создается из канала. Для начала воспользуйтесь GrpcChannel.ForAddress
, чтобы создать канал, а затем используйте канал для создания клиента gRPC:
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greet.GreeterClient(channel);
Канал представляет собой долгосрочное подключение к службе gRPC. Создаваемый канал настраивается с параметрами с учетом вызова службы. Например, HttpClient
, используемый для выполнения вызовов, максимальный размер сообщения для отправки и получения, а также ведение журнала можно указать в GrpcChannelOptions
и использовать с 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
Настройка TLS
Клиент gRPC должен использовать ту же систему безопасности на уровне подключения, что и вызванная служба. Протокол TLS для клиента gRPC настраивается при создании канала gRPC. Если система безопасности на уровне подключения канала и службы не совпадает, при вызове службы от клиента gRPC поступает сообщение об ошибке.
Чтобы настроить канал gRPC для использования протокола TLS, убедитесь, что адрес сервера начинается с https
. Например, GrpcChannel.ForAddress("https://localhost:5001")
использует протокол HTTPS. Канал gRPC автоматически согласует подключение, защищенное с помощью TLS, и использует безопасное соединение для выполнения вызовов gRPC.
Совет
gRPC поддерживает проверку подлинности на основе сертификата клиента по протоколу TLS. Сведения о настройке сертификатов клиента с помощью канала gRPC см. в статье Проверка подлинности и авторизация в gRPC для ASP.NET Core.
Чтобы вызвать незащищенные службы gRPC, убедитесь, что адрес сервера начинается с http
. Например, GrpcChannel.ForAddress("http://localhost:5000")
использует протокол HTTP. В .NET Core 3.1 для вызова незащищенных служб gRPC с помощью клиента .NET требуется дополнительная настройка.
Производительность клиента
Производительность и использование канала и клиента:
- Создание канала может потребовать значительных ресурсов. Повторное использование канала для вызовов gRPC обеспечивает выигрыш в производительности.
- Канал управляет подключениями к серверу. Если подключение закрыто или потеряно, канал автоматически повторно подключается при следующем вызове gRPC.
- Клиенты gRPC создаются с помощью каналов. Клиенты gRPC являются облегченными объектами и не нуждаются в кэшировании или повторном использовании.
- Из одного канала можно создать несколько клиентов gRPC, включая различные типы клиентов.
- Канал и клиенты, созданные из канала, могут безопасно использоваться несколькими потоками.
- Клиенты, созданные из канала, могут выполнять несколько одновременных вызовов.
GrpcChannel.ForAddress
— не единственный вариант создания клиента gRPC. При вызове службы gRPC из приложения ASP.NET Core, рассмотрите возможность интеграции фабрики клиента gRPC. Интеграция gRPC с HttpClientFactory
предлагает централизованную альтернативу созданию клиентов gRPC.
Примечание.
Вызов gRPC через HTTP/2 с Grpc.Net.Client
в настоящее время не поддерживается в Xamarin. Мы работаем над улучшением поддержки HTTP/2 в будущих выпусках Xamarin. Grpc.Core и gRPC-Web являются приемлемыми работающими альтернативами, которые доступны на сегодняшний день.
Вызовы 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
файле приведет к двум методам .NET для конкретного типа клиента gRPC для вызова метода: асинхронного метода и метода блокировки. Например, в 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
}
Тип, возвращаемый при запуске вызова потоковой передачи IDisposable
сервера, реализуется. Всегда удаляйте вызов потоковой передачи, чтобы убедиться, что он остановлен и все ресурсы очищаются.
Вызов потоковой передачи клиента
Вызов потоковой передачи клиента начинается без клиента, отправляющего сообщение с запросом. Клиент может выбрать отправку сообщений с помощью 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
Тип, возвращаемый при запуске вызова потоковой передачи IDisposable
клиента, реализует. Всегда удаляйте вызов потоковой передачи, чтобы убедиться, что он остановлен и все ресурсы очищаются.
Вызов двунаправленной потоковой передачи
Вызов двунаправленной потоковой передачи начинается без клиента, отправляющего сообщение с запросом. Клиент может выбрать отправку сообщений с помощью RequestStream.WriteAsync
. Сообщения, переданные в службу путем потоковой передачи, доступны с ResponseStream.MoveNext()
или ResponseStream.ReadAllAsync()
. Вызов двунаправленной потоковой передачи завершается, когда ResponseStream
больше не содержит сообщений.
var client = new Echo.EchoClient(channel);
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;
Чтобы обеспечить наилучшую производительность и избежать ненужных ошибок в клиенте и службе, старайтесь правильно выполнять двунаправленные потоковые вызовы. Двунаправленный вызов завершается корректно, когда сервер завершил чтение потока запроса, а клиент завершил чтение потока ответа. Предыдущий пример вызова — это один из примеров двунаправленного вызова, который завершается корректно. При вызове клиент:
- Запускает новый двунаправленный потоковый вызов путем вызова
EchoClient.Echo
. - Создает фоновую задачу для чтения сообщений из службы с помощью
ResponseStream.ReadAllAsync()
. - Отправляет сообщения на сервер с помощью
RequestStream.WriteAsync
. - Сообщает серверу, что он закончил отправку сообщений с помощью
RequestStream.CompleteAsync()
. - Ожидает, пока фоновая задача не прочитает все входящие сообщения.
Во время вызова двунаправленной потоковой передачи клиент и служба могут обмениваться сообщениями в любое время. Наиболее подходящая логика клиента для взаимодействия с вызовом двунаправленной потоковой передачи зависит от логики службы.
Тип, возвращаемый при запуске двунаправленного вызова потоковой передачи IDisposable
, реализуется. Всегда удаляйте вызов потоковой передачи, чтобы убедиться, что он остановлен и все ресурсы очищаются.
Доступ к заголовкам gRPC
Вызовы gRPC возвращают заголовки ответа. Заголовки ответа HTTP передают метаданные (имя и значение) вызова без связи с возвращаемым сообщением.
Заголовки gRPC доступны при использовании ResponseHeadersAsync
(возвращает коллекцию метаданных). Заголовки обычно возвращаются с ответным сообщением. Поэтому их необходимо ожидать.
var client = new Greet.GreeterClient(channel);
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
var headers = await call.ResponseHeadersAsync;
var myValue = headers.GetValue("my-trailer-name");
var response = await call.ResponseAsync;
При использовании ResponseHeadersAsync
:
- Нужно дождаться результата
ResponseHeadersAsync
, чтобы получить коллекцию заголовков. - Не нужно получать доступ перед
ResponseAsync
(или потока ответа при потоковой передаче). Если ответ был получен,ResponseHeadersAsync
немедленно возвращает заголовки. - Выдается исключение, если возникла ошибка подключения или сервера и для вызова gRPC заголовки не получены.
Доступ к трейлерам gRPC
Вызовы gRPC могут возвращать трейлеры ответа. Трейлеры содержат метаданные (имя и значение) вызова. Трейлеры содержат аналогичные функции для заголовков HTTP, но они принимаются в конце вызова.
Трейлеры доступны при использовании GetTrailers()
(возвращает коллекцию метаданных). Трейлеры возвращаются после завершения ответа. Поэтому перед обращением к трейлеру необходимо дождаться всех ответных сообщений.
Унарные и клиентские вызовы потоковой передачи должны дождаться ResponseAsync
перед вызовом GetTrailers()
:
var client = new Greet.GreeterClient(channel);
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
var response = await call.ResponseAsync;
Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World
var trailers = call.GetTrailers();
var myValue = trailers.GetValue("my-trailer-name");
Вызовы сервера и двунаправленной потоковой передачи должны завершить ожидание ответного потока перед вызовом GetTrailers()
:
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
}
var trailers = call.GetTrailers();
var myValue = trailers.GetValue("my-trailer-name");
Трейлеры также доступны из RpcException
. Служба может вернуть трейлеры вместе со статусом gRPC, отличным от "ОК". В этой ситуации трейлеры извлекаются из исключения, вызываемого клиентом gRPC:
var client = new Greet.GreeterClient(channel);
string myValue = null;
try
{
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
var response = await call.ResponseAsync;
Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World
var trailers = call.GetTrailers();
myValue = trailers.GetValue("my-trailer-name");
}
catch (RpcException ex)
{
var trailers = ex.Trailers;
myValue = trailers.GetValue("my-trailer-name");
}
Настройка крайнего срока
Настраивать крайний срок в вызове gRPC рекомендуется по той причине, что он ограничивает длительность выполнения вызова. Это позволяет предотвратить бесконечное выполнение служб и исчерпание ресурсов сервера. Крайние сроки — это полезное средство для повышения надежности приложений.
Чтобы задать крайний срок для вызова gRPC, настройте CallOptions.Deadline
:
var client = new Greet.GreeterClient(channel);
try
{
var response = await client.SayHelloAsync(
new HelloRequest { Name = "World" },
deadline: DateTime.UtcNow.AddSeconds(5));
// Greeting: Hello World
Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
Console.WriteLine("Greeting timeout.");
}
Дополнительные сведения см. в статье Надежные службы gRPC с крайними сроками и отменой.
Дополнительные ресурсы
ASP.NET Core