Wywoływanie usług gRPC przy użyciu klienta platformy .NET
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Biblioteka klienta gRPC platformy .NET jest dostępna w pakiecie NuGet Grpc.Net.Client . W tym dokumencie wyjaśniono, jak:
- Skonfiguruj klienta gRPC w celu wywoływania usług gRPC.
- Wykonaj wywołania gRPC do jednoargumentowych, przesyłania strumieniowego serwera, przesyłania strumieniowego klienta i dwukierunkowych metod przesyłania strumieniowego.
Konfigurowanie klienta gRPC
Klienci gRPC to konkretne typy klientów generowane na podstawie plików .proto
. Konkretny klient gRPC ma metody, które przekładają się na usługę gRPC w pliku .proto
. Na przykład usługa o nazwie Greeter
generuje GreeterClient
typ z metodami wywoływania usługi.
Klient gRPC jest tworzony na podstawie kanału. Zacznij od utworzenia GrpcChannel.ForAddress
kanału, a następnie użyj kanału, aby utworzyć klienta gRPC:
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greet.GreeterClient(channel);
Kanał reprezentuje długotrwałe połączenie z usługą gRPC. Po utworzeniu kanału jest skonfigurowany z opcjami związanymi z wywoływaniem usługi. Na przykład HttpClient
używane do nawiązywania wywołań, maksymalny rozmiar komunikatu wysyłania i odbierania oraz rejestrowanie można określić na GrpcChannelOptions
platformie i używać jej z GrpcChannel.ForAddress
programem . Aby uzyskać pełną listę opcji, zobacz Opcje konfiguracji klienta.
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
Konfigurowanie protokołu TLS
Klient gRPC musi używać tych samych zabezpieczeń na poziomie połączenia co wywołana usługa. Protokół TLS (Transport Layer Security) klienta gRPC jest konfigurowany podczas tworzenia kanału gRPC. Klient gRPC zgłasza błąd, gdy wywołuje usługę, a zabezpieczenia na poziomie połączenia kanału i usługi nie są zgodne.
Aby skonfigurować kanał gRPC do używania protokołu TLS, upewnij się, że adres serwera rozpoczyna się od https
. Na przykład GrpcChannel.ForAddress("https://localhost:5001")
używa protokołu HTTPS. Kanał gRPC automatycznie negocjuje połączenie zabezpieczone przez protokół TLS i używa bezpiecznego połączenia do nawiązywania wywołań gRPC.
Napiwek
Usługa gRPC obsługuje uwierzytelnianie certyfikatu klienta za pośrednictwem protokołu TLS. Aby uzyskać informacje na temat konfigurowania certyfikatów klienta za pomocą kanału gRPC, zobacz Uwierzytelnianie i autoryzacja w gRPC dla ASP.NET Core.
Aby wywołać niezabezpieczone usługi gRPC, upewnij się, że adres serwera zaczyna się od http
. Na przykład GrpcChannel.ForAddress("http://localhost:5000")
używa protokołu HTTP. W programie .NET Core 3.1 wymagana jest dodatkowa konfiguracja do wywoływania niezabezpieczonych usług gRPC przy użyciu klienta platformy .NET.
Wydajność klienta
Wydajność i użycie kanału i klienta:
- Tworzenie kanału może być kosztowną operacją. Ponowne używanie kanału dla wywołań gRPC zapewnia korzyści z wydajności.
- Kanał zarządza połączeniami z serwerem. Jeśli połączenie zostanie zamknięte lub utracone, kanał automatycznie ponownie nawiąż połączenie przy następnym wywołaniu gRPC.
- Klienci gRPC są tworzone za pomocą kanałów. Klienci gRPC są lekkimi obiektami i nie muszą być buforowane ani ponownie używane.
- Na podstawie kanału można utworzyć wielu klientów gRPC, w tym różnych typów klientów.
- Kanał i klienci utworzoni na podstawie kanału mogą być bezpiecznie używane przez wiele wątków.
- Klienci utworzoni na podstawie kanału mogą wykonywać wiele równoczesnych wywołań.
GrpcChannel.ForAddress
nie jest jedyną opcją tworzenia klienta gRPC. W przypadku wywoływania usług gRPC z aplikacji ASP.NET Core należy rozważyć integrację z fabryką klienta gRPC. Integracja gRPC z usługą HttpClientFactory
oferuje scentralizowaną alternatywę dla tworzenia klientów gRPC.
Uwaga
Wywoływanie funkcji gRPC za pośrednictwem protokołu HTTP/2 z Grpc.Net.Client
programem nie jest obecnie obsługiwane na platformie Xamarin. Pracujemy nad ulepszeniem obsługi protokołu HTTP/2 w przyszłej wersji platformy Xamarin. Grpc.Core i gRPC-Web są realnymi alternatywami, które działają dzisiaj.
Nawiązywanie wywołań gRPC
Wywołanie gRPC jest inicjowane przez wywołanie metody na kliencie. Klient gRPC będzie obsługiwał serializacji komunikatów i zwraca się do wywołania gRPC do właściwej usługi.
GRPC ma różne typy metod. Sposób użycia klienta do wywołania gRPC zależy od typu wywoływanej metody. Typy metod gRPC to:
- Jednoargumentowy
- Przesyłanie strumieniowe serwera
- Przesyłanie strumieniowe klienta
- Dwukierunkowe przesyłanie strumieniowe
Jednoargumentowe połączenie
Jednoargumentowe wywołanie rozpoczyna się od klienta wysyłającego komunikat żądania. Po zakończeniu działania usługi zostanie zwrócony komunikat odpowiedzi.
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
Console.WriteLine("Greeting: " + response.Message);
// Greeting: Hello World
Każda metoda usługi jednoargumentowej w .proto
pliku spowoduje utworzenie dwóch metod platformy .NET na konkretnym typie klienta gRPC do wywoływania metody: metody asynchronicznej i metody blokującej. Na przykład GreeterClient
istnieją dwa sposoby wywoływania metody SayHello
:
GreeterClient.SayHelloAsync
- wywołujeGreeter.SayHello
usługę asynchronicznie. Można oczekiwać.GreeterClient.SayHello
- wywołujeGreeter.SayHello
usługę i blokuje do ukończenia. Nie używaj w kodzie asynchronicznym.
Wywołanie przesyłania strumieniowego serwera
Wywołanie przesyłania strumieniowego serwera rozpoczyna się od klienta wysyłającego komunikat żądania. ResponseStream.MoveNext()
odczytuje komunikaty przesyłane strumieniowo z usługi. Wywołanie przesyłania strumieniowego serwera jest ukończone, gdy ResponseStream.MoveNext()
zwraca wartość 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
}
W przypadku korzystania z języka C# 8 lub nowszego await foreach
składnia może służyć do odczytywania komunikatów. Metoda IAsyncStreamReader<T>.ReadAllAsync()
rozszerzenia odczytuje wszystkie komunikaty ze strumienia odpowiedzi:
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
}
Typ zwrócony od uruchomienia wywołania przesyłania strumieniowego serwera implementuje IDisposable
element . Zawsze usuwaj wywołanie przesyłania strumieniowego, aby upewnić się, że jest zatrzymany, a wszystkie zasoby są czyszczone.
Wywołanie przesyłania strumieniowego klienta
Wywołanie przesyłania strumieniowego klienta jest uruchamiane bez wysyłania komunikatu przez klienta. Klient może wybrać wysyłanie komunikatów za pomocą RequestStream.WriteAsync
polecenia . Po zakończeniu wysyłania komunikatów przez klienta należy wywołać polecenie w RequestStream.CompleteAsync()
celu powiadomienia usługi. Wywołanie jest zakończone, gdy usługa zwraca komunikat odpowiedzi.
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
Typ zwrócony od uruchomienia wywołania przesyłania strumieniowego klienta implementuje IDisposable
element . Zawsze usuwaj wywołanie przesyłania strumieniowego, aby upewnić się, że jest zatrzymany, a wszystkie zasoby są czyszczone.
Dwukierunkowe wywołanie przesyłania strumieniowego
Dwukierunkowe wywołanie przesyłania strumieniowego rozpoczyna się bez wysyłania komunikatu przez klienta. Klient może wybrać wysyłanie komunikatów za pomocą RequestStream.WriteAsync
polecenia . Komunikaty przesyłane strumieniowo z usługi są dostępne za pomocą ResponseStream.MoveNext()
polecenia lub ResponseStream.ReadAllAsync()
. Dwukierunkowe wywołanie przesyłania strumieniowego jest ukończone, gdy ResponseStream
nie ma więcej komunikatów.
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;
Aby uzyskać najlepszą wydajność i uniknąć niepotrzebnych błędów w kliencie i usłudze, spróbuj bezpiecznie wykonać dwukierunkowe wywołania przesyłania strumieniowego. Wywołanie dwukierunkowe kończy się bezpiecznie po zakończeniu odczytywania strumienia żądania przez serwer, a klient zakończył odczytywanie strumienia odpowiedzi. Poprzednie przykładowe wywołanie jest jednym z przykładów dwukierunkowego wywołania, które kończy się bezpiecznie. W wywołaniu klient:
- Uruchamia nowe dwukierunkowe wywołanie przesyłania strumieniowego przez wywołanie metody
EchoClient.Echo
. - Tworzy zadanie w tle do odczytywania komunikatów z usługi przy użyciu polecenia
ResponseStream.ReadAllAsync()
. - Wysyła komunikaty do serwera za pomocą polecenia
RequestStream.WriteAsync
. - Powiadamia serwer, na który zakończył wysyłanie komunikatów za pomocą polecenia
RequestStream.CompleteAsync()
. - Czeka, aż zadanie w tle odczytuje wszystkie komunikaty przychodzące.
Podczas dwukierunkowego wywołania przesyłania strumieniowego klient i usługa mogą wysyłać komunikaty do siebie w dowolnym momencie. Najlepsza logika klienta do interakcji z wywołaniem dwukierunkowym różni się w zależności od logiki usługi.
Typ zwracany od uruchomienia dwukierunkowego wywołania przesyłania strumieniowego implementuje IDisposable
wartość . Zawsze usuwaj wywołanie przesyłania strumieniowego, aby upewnić się, że jest zatrzymany, a wszystkie zasoby są czyszczone.
Uzyskiwanie dostępu do nagłówków gRPC
GRPC wywołuje nagłówki odpowiedzi zwracanej. Nagłówki odpowiedzi HTTP przekazują metadane nazwy/wartości dotyczące wywołania, które nie jest powiązane z zwróconym komunikatem.
Nagłówki są dostępne przy użyciu metody ResponseHeadersAsync
, która zwraca kolekcję metadanych. Nagłówki są zwykle zwracane z komunikatem odpowiedzi; dlatego trzeba je czekać.
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
zwyczaj:
- Musi czekać na wynik
ResponseHeadersAsync
pobierania kolekcji nagłówków. - Nie trzeba uzyskiwać dostępu przed
ResponseAsync
(ani strumienia odpowiedzi podczas przesyłania strumieniowego). Jeśli odpowiedź została zwrócona,ResponseHeadersAsync
zwraca nagłówki natychmiast. - Zgłosi wyjątek, jeśli wystąpił błąd połączenia lub serwera i nagłówki nie zostały zwrócone dla wywołania gRPC.
Dostęp do przyczep gRPC
Wywołania gRPC mogą zwracać przyczepy odpowiedzi. Przyczepy służą do podawania metadanych nazwy/wartości dotyczących wywołania. Przyczepy zapewniają podobne funkcje do nagłówków HTTP, ale są odbierane na końcu wywołania.
Przyczepy są dostępne przy użyciu elementu GetTrailers()
, który zwraca kolekcję metadanych. Przyczepy są zwracane po zakończeniu odpowiedzi. W związku z tym należy czekać na wszystkie komunikaty odpowiedzi przed uzyskaniem dostępu do zwiastunów.
Wywołania przesyłania strumieniowego jednoargumentowego i klienta muszą czekać ResponseAsync
przed wywołaniem metody 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");
Wywołania serwera i dwukierunkowego przesyłania strumieniowego muszą zakończyć oczekiwanie na strumień odpowiedzi przed wywołaniem metody 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");
Przyczepy są również dostępne z RpcException
witryny . Usługa może zwracać przyczepy wraz ze stanem gRPC innej niż OK. W takiej sytuacji przyczepy są pobierane z wyjątku zgłaszanego przez klienta 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");
}
Konfigurowanie terminu ostatecznego
Zalecane jest skonfigurowanie terminu ostatecznego wywołania gRPC, ponieważ zapewnia górny limit czasu uruchomienia wywołania. Przestaje działać nieprawidłowo działające usługi na zawsze i wyczerpać zasoby serwera. Terminy ostateczne to przydatne narzędzie do tworzenia niezawodnych aplikacji.
Skonfiguruj CallOptions.Deadline
, aby ustawić termin ostatecznego wywołania gRPC:
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.");
}
Aby uzyskać więcej informacji, zobacz Reliable gRPC services with deadline and cancellation (Niezawodne usługi gRPC z terminami i anulowaniem).