gRPC-Clientfactoryintegration in .NET

Von James Newton-King

Die gRPC-Integration mit HttpClientFactory bietet eine zentralisierte Möglichkeit, gRPC-Clients zu erstellen. Diese Möglichkeit kann als Alternative zum Konfigurieren eigenständiger gRPC-Clientinstanzen verwendet werden. Die Factoryintegration ist im NuGet-Paket Grpc.Net.ClientFactory verfügbar.

Die Factory bietet die folgenden Vorteile:

  • Ein zentraler Ort für das Konfigurieren logischer gRPC-Clientinstanzen
  • Optionen für die Verwaltung der Lebensdauer des zugrunde liegenden HttpClientMessageHandler
  • Automatische Weitergabe von Stichtagen und Absagen in einem ASP.NET Core-gRPC-Dienst

Registrierung von gRPC-Clients

Zum Registrieren eines gRPC-Clients kann die generische Erweiterungsmethode AddGrpcClient innerhalb einer Instanz von WebApplicationBuilder beim Einstiegspunkt der App in Program.cs verwendet werden, wobei die typisierte Klasse des gRPC-Clients und die Dienstadresse angegeben wird:

builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

Der gRPC-Clienttyp wird als vorübergehender Typ mit einer Abhängigkeitsinjektion (Dependency Injection, DI) registriert. Der Client kann nun injiziert und direkt in von der DI erstellten Typen genutzt werden. Bei ASP.NET Core-MVC-Controllern, SignalR-Hubs und gRPC-Diensten handelt es sich um Orte, an denen gRPC-Clients direkt injiziert werden können:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

Konfigurieren von HttpHandler

HttpClientFactory erstellt die HttpMessageHandler-Klasse, die vom gRPC-Client verwendet wird. HttpClientFactory-Standardmethoden können verwendet werden ,um Middleware für ausgehende Anforderungen hinzuzufügen oder den zugrunde liegenden HttpClientHandler der HttpClient-Klasse zu konfigurieren:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

Weitere Informationen erhalten Sie unter Stellen von HTTP-Anforderungen mithilfe von IHttpClientFactory in ASP.NET Core.

Konfigurieren von Interceptors

gRPC-Interceptors können Clients mithilfe der AddInterceptor-Methode hinzugefügt werden.

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>();

Der obige Code:

  • Registriert den Typ GreeterClient.
  • Konfiguriert einen LoggingInterceptor für diesen Client. LoggingInterceptor wird einmal erstellt und von GreeterClient-Instanzen gemeinsam genutzt.

Standardmäßig wird ein Interceptor einmal erstellt und von Clients gemeinsam genutzt. Dieses Verhalten kann überschrieben werden, indem beim Registrieren eines Interceptors ein Bereich angeben wird. Die Clientfactory kann so konfiguriert werden, dass für jeden Client ein neuer Interceptor erstellt wird, indem InterceptorScope.Client angegeben wird.

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>(InterceptorScope.Client);

Die Erstellung clientbezogener Interceptors ist nützlich, wenn ein Interceptor bereichsbezogene oder vorübergehende Dienste von DI benötigt.

Ein gRPC-Interceptor oder Kanalanmeldeinformationen können verwendet werden, um Authorization-Metadaten mit jeder Anforderung zu senden. Weitere Informationen zum Konfigurieren von Authentifizierung finden Sie unter Senden eines Bearertokens mit der gRPC-Clientfactory.

Konfigurieren eines Kanals

Zusätzliche Konfigurationen können mit der Methode ConfigureChannel auf einen Kanal angewendet werden:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

ConfigureChannel wird eine GrpcChannelOptions-Instanz übergeben. Weitere Informationen finden Sie unter Konfigurieren von Clientoptionen.

Hinweis

Einige Eigenschaften werden auf GrpcChannelOptions festgelegt, bevor der Rückruf ConfigureChannel ausgeführt wird:

Diese Werte können von ConfigureChannel überschrieben werden.

Aufrufen von Anmeldeinformationen

Ein Authentifizierungsheader kann gRPC-Aufrufen mithilfe der AddCallCredentials Methode hinzugefügt werden:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

Weitere Informationen zum Konfigurieren von Aufrufanmeldeinformationen finden Sie unter Bearertoken mit der gRPC-Clientfactory.

Weitergabe von Stichtagen und Absagen

gRPC-Clients, die von der Factory in einem gRPC-Dienst erstellt wurden, können mit EnableCallContextPropagation() so konfiguriert werden, dass sie die Tokens für Stichtage und Absagen automatisch an untergeordnete Aufrufe weitergeben. Die Erweiterungsmethode EnableCallContextPropagation() ist im NuGet-Paket Grpc.AspNetCore.Server.ClientFactory verfügbar.

Die Weitergabe von Aufrufkontext funktioniert, in dem die Tokens für Stichtage und Absagen aus dem aktuellen gRPC-Anforderungskontext gelesen und automatisch an ausgehende Aufrufe weitergegeben werden, die vom gRPC-Client ausgeführt werden. Die Weitergabe von Aufrufkontext ist eine sehr gute Möglichkeit, sicherzustellen, dass komplexe und verschachtelte gRPC-Szenarios immer den Stichtag und die Absage weitergeben.

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

EnableCallContextPropagation löst standardmäßig einen Fehler aus, wenn der Client außerhalb von gRPC-Aufrufen verwendet wird. Der Fehler ist dazu konzipiert, Sie darüber zu informieren, dass kein Aufrufkontext zum Übertragen vorhanden ist. Wenn Sie den Client außerhalb eines Aufrufkontexts verwenden möchten, unterdrücken Sie den Fehler, wenn der Client mit SuppressContextNotFoundErrors konfiguriert ist:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);

Weitere Informationen zu Fristen und RPC-Abbruch finden Sie unter Zuverlässige gRPC-Dienste mit Fristen und Abbrüchen.

Benannte Clients

In der Regel wird ein gRPC-Clienttyp einmal registriert und dann von DI direkt in den Konstruktor eines Typs eingefügt. Es gibt jedoch Szenarien, in denen es hilfreich ist, mehrere Konfigurationen für einen Client zu verwenden, z. B. ein Client, der gRPC-Aufrufe mit und ohne Authentifizierung durchführt.

Mehrere Clients mit demselben Typ können registriert werden, indem jedem Client ein Name zugewiesen wird. Jeder benannte Client kann über eine eigene Konfiguration verfügen. Die generische AddGrpcClient-Erweiterungsmethode umfasst eine Überladung, die einen Namensparameter enthält:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

Der obige Code:

  • Registriert den GreeterClient-Typ zweimal, wobei jeweils ein eindeutiger Name angegeben wird.
  • Konfiguriert unterschiedliche Einstellungen für jeden benannten Client. Die GreeterAuthenticated-Registrierung fügt Anmeldeinformationen zum Kanal hinzu, damit entsprechende gRPC-Aufrufe authentifiziert werden.

Ein benannter gRPC-Client wird im App-Code mithilfe von GrpcClientFactory erstellt. Typ und Name des gewünschten Clients werden mithilfe der generischen GrpcClientFactory.CreateClient-Methode angegeben:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

Zusätzliche Ressourcen

Die gRPC-Integration mit HttpClientFactory bietet eine zentralisierte Möglichkeit, gRPC-Clients zu erstellen. Diese Möglichkeit kann als Alternative zum Konfigurieren eigenständiger gRPC-Clientinstanzen verwendet werden. Die Factoryintegration ist im NuGet-Paket Grpc.Net.ClientFactory verfügbar.

Die Factory bietet die folgenden Vorteile:

  • Ein zentraler Ort für das Konfigurieren logischer gRPC-Clientinstanzen
  • Optionen für die Verwaltung der Lebensdauer des zugrunde liegenden HttpClientMessageHandler
  • Automatische Weitergabe von Stichtagen und Absagen in einem ASP.NET Core-gRPC-Dienst

Registrierung von gRPC-Clients

Zum Registrieren eines gRPC-Clients kann die generische Erweiterungsmethode AddGrpcClient innerhalb von Startup.ConfigureServices verwendet werden, die die typisierte Klasse des gRPC-Clients und die Dienstadresse angibt:

services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

Der gRPC-Clienttyp wird als vorübergehender Typ mit einer Abhängigkeitsinjektion (Dependency Injection, DI) registriert. Der Client kann nun injiziert und direkt in von der DI erstellten Typen genutzt werden. Bei ASP.NET Core-MVC-Controllern, SignalR-Hubs und gRPC-Diensten handelt es sich um Orte, an denen gRPC-Clients direkt injiziert werden können:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

Konfigurieren von HttpHandler

HttpClientFactory erstellt die HttpMessageHandler-Klasse, die vom gRPC-Client verwendet wird. HttpClientFactory-Standardmethoden können verwendet werden ,um Middleware für ausgehende Anforderungen hinzuzufügen oder den zugrunde liegenden HttpClientHandler der HttpClient-Klasse zu konfigurieren:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

Weitere Informationen erhalten Sie unter Stellen von HTTP-Anforderungen mithilfe von IHttpClientFactory in ASP.NET Core.

Konfigurieren von Interceptors

gRPC-Interceptors können Clients mithilfe der AddInterceptor-Methode hinzugefügt werden.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>();

Der obige Code:

  • Registriert den Typ GreeterClient.
  • Konfiguriert einen LoggingInterceptor für diesen Client. LoggingInterceptor wird einmal erstellt und von GreeterClient-Instanzen gemeinsam genutzt.

Standardmäßig wird ein Interceptor einmal erstellt und von Clients gemeinsam genutzt. Dieses Verhalten kann überschrieben werden, indem beim Registrieren eines Interceptors ein Bereich angeben wird. Die Clientfactory kann so konfiguriert werden, dass für jeden Client ein neuer Interceptor erstellt wird, indem InterceptorScope.Client angegeben wird.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>(InterceptorScope.Client);

Die Erstellung clientbezogener Interceptors ist nützlich, wenn ein Interceptor bereichsbezogene oder vorübergehende Dienste von DI benötigt.

Ein gRPC-Interceptor oder Kanalanmeldeinformationen können verwendet werden, um Authorization-Metadaten mit jeder Anforderung zu senden. Weitere Informationen zum Konfigurieren von Authentifizierung finden Sie unter Senden eines Bearertokens mit der gRPC-Clientfactory.

Konfigurieren eines Kanals

Zusätzliche Konfigurationen können mit der Methode ConfigureChannel auf einen Kanal angewendet werden:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

ConfigureChannel wird eine GrpcChannelOptions-Instanz übergeben. Weitere Informationen finden Sie unter Konfigurieren von Clientoptionen.

Hinweis

Einige Eigenschaften werden auf GrpcChannelOptions festgelegt, bevor der Rückruf ConfigureChannel ausgeführt wird:

Diese Werte können von ConfigureChannel überschrieben werden.

Weitergabe von Stichtagen und Absagen

gRPC-Clients, die von der Factory in einem gRPC-Dienst erstellt wurden, können mit EnableCallContextPropagation() so konfiguriert werden, dass sie die Tokens für Stichtage und Absagen automatisch an untergeordnete Aufrufe weitergeben. Die Erweiterungsmethode EnableCallContextPropagation() ist im NuGet-Paket Grpc.AspNetCore.Server.ClientFactory verfügbar.

Die Weitergabe von Aufrufkontext funktioniert, in dem die Tokens für Stichtage und Absagen aus dem aktuellen gRPC-Anforderungskontext gelesen und automatisch an ausgehende Aufrufe weitergegeben werden, die vom gRPC-Client ausgeführt werden. Die Weitergabe von Aufrufkontext ist eine sehr gute Möglichkeit, sicherzustellen, dass komplexe und verschachtelte gRPC-Szenarios immer den Stichtag und die Absage weitergeben.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

EnableCallContextPropagation löst standardmäßig einen Fehler aus, wenn der Client außerhalb von gRPC-Aufrufen verwendet wird. Der Fehler ist dazu konzipiert, Sie darüber zu informieren, dass kein Aufrufkontext zum Übertragen vorhanden ist. Wenn Sie den Client außerhalb eines Aufrufkontexts verwenden möchten, unterdrücken Sie den Fehler, wenn der Client mit SuppressContextNotFoundErrors konfiguriert ist:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);

Weitere Informationen zu Fristen und RPC-Abbruch finden Sie unter Zuverlässige gRPC-Dienste mit Fristen und Abbrüchen.

Benannte Clients

In der Regel wird ein gRPC-Clienttyp einmal registriert und dann von DI direkt in den Konstruktor eines Typs eingefügt. Es gibt jedoch Szenarien, in denen es hilfreich ist, mehrere Konfigurationen für einen Client zu verwenden, z. B. ein Client, der gRPC-Aufrufe mit und ohne Authentifizierung durchführt.

Mehrere Clients mit demselben Typ können registriert werden, indem jedem Client ein Name zugewiesen wird. Jeder benannte Client kann über eine eigene Konfiguration verfügen. Die generische AddGrpcClient-Erweiterungsmethode umfasst eine Überladung, die einen Namensparameter enthält:

services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });

services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

Der obige Code:

  • Registriert den GreeterClient-Typ zweimal, wobei jeweils ein eindeutiger Name angegeben wird.
  • Konfiguriert unterschiedliche Einstellungen für jeden benannten Client. Die GreeterAuthenticated-Registrierung fügt Anmeldeinformationen zum Kanal hinzu, damit entsprechende gRPC-Aufrufe authentifiziert werden.

Ein benannter gRPC-Client wird im App-Code mithilfe von GrpcClientFactory erstellt. Typ und Name des gewünschten Clients werden mithilfe der generischen GrpcClientFactory.CreateClient-Methode angegeben:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

Zusätzliche Ressourcen