Sdílet prostřednictvím


Průsečíky gRPC v .NET

Autor: Ernst Nguyen

Průsečíky jsou koncept gRPC, který umožňuje aplikacím pracovat s příchozími nebo odchozími voláními gRPC. Nabízejí způsob, jak rozšířit kanál zpracování požadavků.

Průsečíky jsou nakonfigurované pro kanál nebo službu a spouští se automaticky pro každé volání gRPC. Vzhledem k tomu, že průsečíky jsou pro logiku aplikace uživatele transparentní, představují vynikající řešení pro běžné případy, jako je protokolování, monitorování, ověřování a ověřování.

Typ Interceptor

Zachytávání lze implementovat pro servery gRPC i klienty vytvořením třídy, která dědí z Interceptor typu:

public class ExampleInterceptor : Interceptor
{
}

Ve výchozím nastavení Interceptor základní třída nic nedělá. Přidejte chování do průsečíku přepsáním odpovídajících metod základní třídy v implementaci zachytávání.

Zachytávání klientů

Zachycovače klienta gRPC zachycují odchozí volání RPC. Poskytují přístup k odeslané žádosti, příchozí odpovědi a kontextu pro volání na straně klienta.

Interceptor metody přepsání klienta:

  • BlockingUnaryCall: Zachytí blokující vyvolání unárního RPC.
  • AsyncUnaryCall: Zachytí asynchronní vyvolání unárního RPC.
  • AsyncClientStreamingCall: Zachytí asynchronní vyvolání rpc streamování klienta.
  • AsyncServerStreamingCall: Zachytí asynchronní vyvolání rpc streamování serveru.
  • AsyncDuplexStreamingCall: Zachytí asynchronní vyvolání obousměrného streamování RPC.

Upozorňující

I když obě BlockingUnaryCall a AsyncUnaryCall odkazují na unární rpcs, nejsou zaměnitelné. Blokující vyvolání není zachycováno AsyncUnaryCalla asynchronní vyvolání není zachyceno BlockingUnaryCallfunkcí .

Vytvoření zachytávání gRPC klienta

Následující kód představuje základní příklad zachycení asynchronního vyvolání unárního volání:

public class ClientLoggingInterceptor : Interceptor
{
    private readonly ILogger _logger;

    public ClientLoggingInterceptor(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<ClientLoggingInterceptor>();
    }

    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        _logger.LogInformation("Starting call. Type/Method: {Type} / {Method}",
            context.Method.Type, context.Method.Name);
        return continuation(request, context);
    }
}

Přepsání AsyncUnaryCall:

  • Zachycuje asynchronní unární volání.
  • Zaznamenává podrobnosti o hovoru.
  • Volá parametr předaný continuation do metody. Tím se vyvolá další zachytávání v řetězci nebo v podkladovém volajícím, pokud se jedná o poslední průsečík.

Interceptor Metody pro každý druh metody služby mají různé podpisy. Koncept a continuationcontext parametry však zůstávají stejné:

  • continuation je delegát, který vyvolá další zachytávání v řetězu nebo vyvolání základního volání (pokud v řetězci není ponechán žádný průsečík). Nejedná se o chybu, která by ji volala nulou nebo vícekrát. Průsečíky se nevyžadují k vrácení reprezentace volání (AsyncUnaryCall v případě unárního RPC) vráceného delegátem continuation . Vynechání volání delegáta a vrácení vlastní instance reprezentace volání přeruší řetěz průsečíků a vrátí přidruženou odpověď okamžitě.
  • context provádí vymezené hodnoty spojené s voláním na straně klienta. Slouží context k předávání metadat, jako jsou objekty zabezpečení, přihlašovací údaje nebo data trasování. Kromě toho context přináší informace o konečných termínech a zrušení. Další informace najdete v tématu Spolehlivé služby gRPC s termíny a zrušením.

Čekání na odpověď v zachytávání klienta

Zachytávání může čekat na odpověď v unárních a klientských streamovacích voláních aktualizací AsyncUnaryCall<TResponse>.ResponseAsync nebo AsyncClientStreamingCall<TRequest, TResponse>.ResponseAsync hodnoty.

public class ErrorHandlerInterceptor : Interceptor
{
    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        var call = continuation(request, context);

        return new AsyncUnaryCall<TResponse>(
            HandleResponse(call.ResponseAsync),
            call.ResponseHeadersAsync,
            call.GetStatus,
            call.GetTrailers,
            call.Dispose);
    }

    private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> inner)
    {
        try
        {
            return await inner;
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException("Custom error", ex);
        }
    }
}

Předchozí kód:

  • Vytvoří nový průsečík AsyncUnaryCall, který přepíše .
  • Přepsání AsyncUnaryCall:
    • Zavolá parametr, continuation který vyvolá další položku v řetězu průsečíku.
    • Vytvoří novou AsyncUnaryCall<TResponse> instanci na základě výsledku pokračování.
    • Zabalí ResponseAsync úlohu pomocí HandleResponse metody.
    • Čeká na odpověď s HandleResponse. Čekání na odpověď umožní přidání logiky po přijetí odpovědi klientem. Čekáním na odpověď v bloku try-catch je možné zaprotokolovat chyby volání.

Další informace o tom, jak vytvořit zachycovač klienta, najdete ClientLoggerInterceptor.cs v příkladu grpc/grpc-dotnet v úložišti GitHub.

Konfigurace zachytávání klientů

Průsečíky klienta gRPC se konfigurují v kanálu.

Následující kód:

  • Vytvoří kanál pomocí .GrpcChannel.ForAddress
  • Intercept Pomocí metody rozšíření nakonfiguruje kanál tak, aby používal průsečík. Všimněte si, že tato metoda vrátí CallInvokerhodnotu . Klienti gRPC silného typu je možné vytvořit z invokeru stejně jako kanál.
  • Vytvoří klienta z invokeru. Volání gRPC provedená klientem automaticky spustí průsečík.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var invoker = channel.Intercept(new ClientLoggerInterceptor());

var client = new Greeter.GreeterClient(invoker);

Metodu Intercept rozšíření je možné zřetězit a nakonfigurovat více průsečíků pro kanál. Alternativně existuje Intercept přetížení, které přijímá více průsečíků. Libovolný počet průsečíků lze spustit pro jedno volání gRPC, jak ukazuje následující příklad:

var invoker = channel
    .Intercept(new ClientTokenInterceptor())
    .Intercept(new ClientMonitoringInterceptor())
    .Intercept(new ClientLoggerInterceptor());

Průsečíky se vyvolávají v obráceném pořadí zřetězených Intercept rozšiřujících metod. V předchozím kódu jsou průsečíky vyvolány v následujícím pořadí:

  1. ClientLoggerInterceptor
  2. ClientMonitoringInterceptor
  3. ClientTokenInterceptor

Informace o konfiguraci průsečíků s klientskou továrnou gRPC najdete v tématu integrace klientské továrny gRPC v .NET.

Zachytávání serverů

Zachytávání serverů gRPC zachycuje příchozí požadavky RPC. Poskytují přístup k příchozímu požadavku, odchozí odpovědi a kontextu volání na straně serveru.

Interceptor metody přepsání pro server:

  • UnaryServerHandler: Zachytí unární RPC.
  • ClientStreamingServerHandler: Zachytí rpc streamování klienta.
  • ServerStreamingServerHandler: Zachytí protokol RPC streamování serveru.
  • DuplexStreamingServerHandler: Zachytí obousměrné streamování RPC.

Vytvoření zachytávání gRPC serveru

Následující kód představuje příklad zachycení příchozího unárního RPC:

public class ServerLoggerInterceptor : Interceptor
{
    private readonly ILogger _logger;

    public ServerLoggerInterceptor(ILogger<ServerLoggerInterceptor> logger)
    {
        _logger = logger;
    }

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        _logger.LogInformation("Starting receiving call. Type/Method: {Type} / {Method}",
            MethodType.Unary, context.Method);
        try
        {
            return await continuation(request, context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error thrown by {context.Method}.");
            throw;
        }
    }
}

Přepsání UnaryServerHandler:

  • Zachytí příchozí unární hovor.
  • Zaznamenává podrobnosti o hovoru.
  • Volá parametr předaný continuation do metody. Tím se vyvolá další zachytávání v řetězu nebo obslužné rutině služby, pokud se jedná o poslední průsečík.
  • Zaznamená všechny výjimky. Čekání na pokračování umožní přidání logiky po spuštění metody služby. Čekáním na pokračování v bloku try-catch je možné protokolovat chyby z metod.

Podpis metod průsečíků klienta i serveru je podobný:

  • continuation je zkratka pro delegáta příchozího RPC volání dalšího průsečíku v řetězu nebo obslužné rutině služby (pokud v řetězu není ponechán žádný průsečík). Podobně jako u zachytávání klienta ji můžete kdykoli zavolat a nemusíte vracet odpověď přímo od delegáta pokračování. Odchozí logiku je možné přidat po spuštění obslužné rutiny služby čekáním na pokračování.
  • context provádí metadata spojená s voláním na straně serveru, jako jsou metadata požadavku, konečné termíny a zrušení nebo výsledek RPC.

Další informace o tom, jak vytvořit zachycovač serveru, najdete ServerLoggerInterceptor.cs v příkladu grpc/grpc-dotnet v úložišti GitHub.

Konfigurace zachytávání serverů

Zachytávání serverů gRPC se konfiguruje při spuštění. Následující kód:

  • Přidá gRPC do aplikace pomocí AddGrpc.
  • Konfiguruje ServerLoggerInterceptor pro všechny služby tak, že ji přidáte do kolekce možností Interceptors služby.
public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc(options =>
    {
        options.Interceptors.Add<ServerLoggerInterceptor>();
    });
}

Zachytávání lze také nakonfigurovat pro konkrétní službu pomocí AddServiceOptions a určení typu služby.

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddGrpc()
        .AddServiceOptions<GreeterService>(options =>
        {
            options.Interceptors.Add<ServerLoggerInterceptor>();
        });
}

Průsečíky se spouštějí v pořadí, v jakém jsou přidány do .InterceptorCollection Pokud jsou nakonfigurované globální i jednoúčelové zachytávání služby, spustí se před konfigurací pro jednu službu globálně nakonfigurované průsečíky.

Ve výchozím nastavení mají průsečíky serverů gRPC životnost podle požadavku. Přepsání tohoto chování je možné prostřednictvím registrace typu průsečíku pomocí injektáže závislostí. Následující příklad zaregistruje životnost jednohotonu ServerLoggerInterceptor :

public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc(options =>
    {
        options.Interceptors.Add<ServerLoggerInterceptor>();
    });

    services.AddSingleton<ServerLoggerInterceptor>();
}

gRPC Interceptors versus Middleware

ASP.NET middleware Core nabízí podobné funkce v porovnání s průsečíky v aplikacích GRPC založených na jádru C. ASP.NET middlewaru Core a průsečíky jsou koncepčně podobné. Obojí:

  • Slouží k vytvoření kanálu, který zpracovává požadavek gRPC.
  • Povolte provedení práce před nebo za další komponentou v kanálu.
  • Poskytnout přístup k HttpContext:
    • V middlewaru je parametr HttpContext .
    • V průsečíkech HttpContext je možné k němu přistupovat pomocí ServerCallContext parametru s metodou ServerCallContext.GetHttpContext rozšíření. Tato funkce je specifická pro průsečíky spuštěné v ASP.NET Core.

Rozdíly průsečíku gRPC od middlewaru ASP.NET Core:

  • Průsečíky:
    • Pracovat s gRPC vrstvy abstrakce pomocí ServerCallContext.
    • Poskytnutí přístupu k:
      • Deserializovaná zpráva byla odeslána volání.
      • Zpráva vrácená z volání před serializována.
    • Dokáže zachytit a zpracovat výjimky vyvolané službami gRPC.
  • Middleware:
    • Spustí se pro všechny požadavky HTTP.
    • Spustí se před průsečíky gRPC.
    • Pracuje s podkladovými zprávami HTTP/2.
    • Může přistupovat pouze k bajtům z datových proudů požadavků a odpovědí.

Další prostředky