Dela via


gRPC-interceptorer på .NET

Anmärkning

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Viktigt!

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Av Ernest Nguyen

Interceptorer är ett gRPC-koncept som gör att appar kan interagera med inkommande eller utgående gRPC-anrop. De erbjuder ett sätt att utöka pipelinen för bearbetning av begäranden.

Interceptorer konfigureras för en kanal eller tjänst och körs automatiskt för varje gRPC-anrop. Eftersom interceptorer är transparenta för användarens programlogik är de en utmärkt lösning för vanliga fall, till exempel loggning, övervakning, autentisering och validering.

Typ: Interceptor

Interceptorer kan implementeras för både gRPC-servrar och klienter genom att skapa en klass som ärver från typen Interceptor :

public class ExampleInterceptor : Interceptor
{
}

Som standard gör basklassen Interceptor ingenting. Lägg till beteende i en interceptor genom att åsidosätta lämpliga basklassmetoder i en interceptor-implementering.

Klientavskärare

gRPC-klientavlyssnare fångar upp utgående RPC-anrop. De ger åtkomst till den skickade begäran, det inkommande svaret och kontexten för ett anrop på klientsidan.

Interceptor metoder som klienten kan åsidosätta:

  • BlockingUnaryCall: Fångar upp ett blockerande anrop av en unary RPC.
  • AsyncUnaryCall: Fångar upp ett asynkront anrop av en enkel RPC.
  • AsyncClientStreamingCall: Fångar upp ett asynkront anrop av en klientströmmande RPC.
  • AsyncServerStreamingCall: Fångar upp ett asynkront anrop av en RPC för serverströmning.
  • AsyncDuplexStreamingCall: Fångar upp en asynkron anrop av en dubbelriktad direktuppspelnings-RPC.

Varning

Även om både BlockingUnaryCall och AsyncUnaryCall refererar till unary RPCs, är de inte utbytbara. Ett blockerande anrop fångas inte upp av AsyncUnaryCalloch ett asynkront anrop fångas inte upp av en BlockingUnaryCall.

Skapa en gRPC-interceptor för klienten

Följande kod visar ett grundläggande exempel på att fånga upp ett asynkront anrop av ett unary-anrop:

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);
    }
}

Åsidosättande AsyncUnaryCall:

  • Fångar upp ett asynkront unary-anrop.
  • Loggar information om anropet.
  • Anropar continuation som är parametern som skickades till metoden. Detta anropar nästa interceptor i kedjan eller den underliggande anroparen om detta är den sista interceptorn.

Metoder för varje typ av tjänstmetod på Interceptor har olika signaturer. Konceptet bakom continuation och context parametrarna förblir dock detsamma:

  • continuation är ett ombud som anropar nästa interceptor i kedjan eller den underliggande anroparen (om det inte finns någon interceptor kvar i kedjan). Det är inte ett fel att anropa det noll gånger eller flera gånger. Det krävs inte att interceptorer returnerar en anropsrepresentation (AsyncUnaryCall vid unary RPC) som återges av delegeringen continuation. Om du utelämnar delegatanropet och istället returnerar din egen instans av anropsrepresentationen, bryts interceptorernas kedja och det associerade svaret returneras omedelbart.
  • context innehåller begränsade värden som är associerade med anropet på klientsidan. Använd context för att skicka metadata, till exempel säkerhetsobjekt, autentiseringsuppgifter eller spårningsdata. Dessutom context innehåller information om tidsfrister och annullering. Mer information finns i Reliable gRPC services with deadlines and cancellation (Tillförlitliga gRPC-tjänster med tidsgränser och annullering).

Väntar på svar i klientens interceptor

En interceptor kan invänta svaret i unära- och klientströmningsanrop genom att uppdatera AsyncUnaryCall<TResponse>.ResponseAsync-värdet eller AsyncClientStreamingCall<TRequest, TResponse>.ResponseAsync-värdet.

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);
        }
    }
}

Föregående kod:

  • Skapar en ny interceptor som åsidosätter AsyncUnaryCall.
  • Överrida AsyncUnaryCall:
    • Anropar parametern continuation för att anropa nästa objekt i interceptor-kedjan.
    • Skapar en ny AsyncUnaryCall<TResponse> instans baserat på resultatet av fortsättningen.
    • Omsluter ResponseAsync uppgiften med hjälp av HandleResponse metoden.
    • Väntar på svaret med HandleResponse. I väntan på svaret kan logik läggas till när klienten har tagit emot svaret. Genom att vänta på svaret i ett try-catch-block kan fel från anrop loggas.

Mer information om hur du skapar en klientavlyssnare ClientLoggerInterceptor.cs finns i exemplet på grpc/grpc-dotnet GitHub-lagringsplatsen.

Konfigurera klientinterceptorer

gRPC-klientinterceptorer konfigureras på en kanal.

Följande kod:

  • Skapar en kanal genom att använda GrpcChannel.ForAddress.
  • Intercept Använder tilläggsmetoden för att konfigurera kanalen så att den använder interceptorn. Observera att den här metoden returnerar en CallInvoker. gRPC-klienter med stark typ kan skapas från en anropare, precis som en kanal.
  • Skapar en klient från anroparen. gRPC-anrop som görs av klienten kör automatiskt interceptorn.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var invoker = channel.Intercept(new ClientLoggerInterceptor());

var client = new Greeter.GreeterClient(invoker);

Tilläggsmetoden Intercept kan länkas för att konfigurera flera interceptorer för en kanal. Det finns också en Intercept överbelastning som accepterar flera interceptorer. Valfritt antal interceptorer kan köras för ett enda gRPC-anrop, vilket visas i följande exempel:

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

Interceptorer anropas i omvänd ordning med de länkade Intercept tilläggsmetoderna. I föregående kod anropas interceptorer i följande ordning:

  1. ClientLoggerInterceptor
  2. ClientMonitoringInterceptor
  3. ClientTokenInterceptor

Information om hur du konfigurerar interceptorer med gRPC-klientfabrik finns i gRPC-klientfabriksintegrering i .NET.

Serveravlyssnare

gRPC-serveravlyssnare fångar upp inkommande RPC-begäranden. De ger åtkomst till den inkommande begäran, det utgående svaret och kontexten för ett anrop på serversidan.

Interceptor metoder för servern att åsidosätta:

  • UnaryServerHandler: Fångar upp en unary RPC.
  • ClientStreamingServerHandler: Fångar upp en RPC för klientströmning.
  • ServerStreamingServerHandler: Avlyssnar en serverströmmande RPC.
  • DuplexStreamingServerHandler: Fångar upp en dubbelriktad direktuppspelnings-RPC.

Skapa en serverinterceptor för gRPC

Följande kod visar ett exempel på en avlyssning av en inkommande unary 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;
        }
    }
}

Åsidosättande UnaryServerHandler:

  • Fångar upp ett inkommande unärt samtal.
  • Loggar information om anropet.
  • Anropar continuation som är parametern som skickades till metoden. Detta anropar nästa interceptor i kedjan eller tjänsthanteraren om detta är den sista interceptorn.
  • Loggar eventuella undantag. I väntan på fortsättningen kan logik läggas till efter att tjänstmetoden har körts. Genom att vänta på fortsättningen i ett try-catch-block kan fel från metoder loggas.

Signaturerna för både klient- och serverinterceptorerna är liknande.

  • continuation står för ett ombud för en inkommande RPC som anropar nästa interceptor i kedjan eller tjänsthanteraren (om det inte finns någon interceptor kvar i kedjan). Precis som med klientinterceptorer kan du anropa det här när som helst, och du behöver inte returnera ett svar direkt från fortsättningsdelegaten. Utgående logik kan läggas till när en tjänsthanterare har körts i väntan på fortsättningen.
  • context innehåller metadata som är associerade med anropet på serversidan, till exempel metadata för begäran, tidsgränser och annullering eller RPC-resultat.

Mer information om hur du skapar en serveravlyssnare finns ServerLoggerInterceptor.cs i exemplet på grpc/grpc-dotnet GitHub-lagringsplatsen.

Konfigurera serveravlyssnare

gRPC-serveravlyssnare konfigureras vid start. Följande kod:

  • Lägger till gRPC i appen med AddGrpc.
  • Konfigurerar ServerLoggerInterceptor för alla tjänster genom att lägga till den i tjänstalternativets Interceptors samling.
public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc(options =>
    {
        options.Interceptors.Add<ServerLoggerInterceptor>();
    });
}

En interceptor kan också konfigureras för en specifik tjänst genom att använda AddServiceOptions och ange tjänsttypen.

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

Interceptorer körs i den ordning som de läggs till i InterceptorCollection. Om både globala och enskilda tjänstavlyssnare konfigureras körs globalt konfigurerade interceptorer innan de konfigureras för en enda tjänst.

I standardläge har gRPC-serverinterceptorer en livslängd som varar per begäran. Att åsidosätta det här beteendet är möjligt genom att registrera interceptor-typen med beroendeinjektion. I följande exempel registreras ServerLoggerInterceptor med en singleton-livslängd:

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

    services.AddSingleton<ServerLoggerInterceptor>();
}

gRPC Interceptors kontra Middleware

ASP.NET Core-mellanprogram erbjuder liknande funktioner jämfört med interceptorer i C-core-baserade gRPC-appar. ASP.NET Core-mellanprogram och interceptorer är konceptuellt lika. Båda:

  • Används för att konstruera en pipeline som hanterar en gRPC-begäran.
  • Tillåt att arbete utförs före eller efter nästa komponent i pipelinen.
  • Ge åtkomst till HttpContext:
    • I mellanmjukvara är HttpContext en parameter.
    • I interceptorer HttpContext kan du komma åt den med hjälp av parametern ServerCallContext med ServerCallContext.GetHttpContext tilläggsmetoden. Den här funktionen är specifik för interceptorer som körs i ASP.NET Core.

Skillnader mellan gRPC Interceptor och ASP.NET Core Middleware.

  • Interceptors
    • Arbeta på gRPC-abstraktionslagret med hjälp av ServerCallContext.
    • Ge åtkomst till:
      • Det deserialiserade meddelandet som skickas till ett anrop.
      • Meddelandet som returnerades från anropet innan det serialiserades.
    • Kan fånga och hantera undantag som genereras från gRPC-tjänster.
  • Mellanprogram:
    • Körs för alla HTTP-begäranden.
    • Körs före gRPC-interceptorer.
    • Fungerar på underliggande HTTP/2-meddelanden.
    • Kan bara komma åt byte från begärande- och svarsströmmarna.

Ytterligare resurser