Integrazione della client factory gRPC in .NET

Di James Newton-King

L'integrazione di gRPC con HttpClientFactory offre un modo centralizzato per creare client gRPC. Può essere usato come alternativa alla configurazione di istanze client gRPC autonome. L'integrazione della factory è disponibile nel pacchetto NuGet Grpc.Net.ClientFactory .

La factory offre i vantaggi seguenti:

  • Fornisce una posizione centrale per la configurazione di istanze client gRPC logiche.
  • Gestisce la durata dell'oggetto sottostante HttpClientMessageHandler.
  • Propagazione automatica della scadenza e dell'annullamento in un servizio gRPC ASP.NET Core.

Registrare client gRPC

Per registrare un client gRPC, il metodo di estensione generico AddGrpcClient può essere usato all'interno di un'istanza di nel punto di WebApplicationBuilder ingresso dell'app in Program.cs, specificando la classe client tipizzata gRPC e l'indirizzo del servizio:

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

Il tipo di client gRPC viene registrato come temporaneo con inserimento di dipendenze (DI). Il client può ora essere inserito e usato direttamente nei tipi creati da DI. ASP.NET Core controller MVC, SignalR hub e servizi gRPC sono posti in cui i client gRPC possono essere inseriti automaticamente:

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

Configurare HttpHandler

HttpClientFactory crea l'oggetto HttpMessageHandler usato dal client gRPC. I metodi standard HttpClientFactory possono essere usati per aggiungere middleware delle richieste in uscita o per configurare il sottostante HttpClientHandler di HttpClient:

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

Per altre informazioni, vedere Effettuare richieste HTTP usando IHttpClientFactory.

Configurare gli intercettori

Gli intercettori gRPC possono essere aggiunti ai client usando il AddInterceptor metodo .

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

Il codice precedente:

  • Registra il GreeterClient tipo.
  • Configura un LoggingInterceptor oggetto per questo client. LoggingInterceptor viene creato una volta e condiviso tra GreeterClient istanze.

Per impostazione predefinita, un intercettatore viene creato una volta e condiviso tra i client. Questo comportamento può essere sottoposto a override specificando un ambito durante la registrazione di un intercettatore. La factory client può essere configurata per creare un nuovo intercettatore per ogni client specificando InterceptorScope.Client.

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

La creazione di intercettori con ambito client è utile quando un intercettatore richiede servizi con ambito o temporanei da DI.

È possibile usare un intercettatore gRPC o le credenziali del canale per inviare Authorization metadati a ogni richiesta. Per altre informazioni sulla configurazione dell'autenticazione, vedere Inviare un token di connessione con gRPC client factory.

Configurare il canale

È possibile applicare una configurazione aggiuntiva a un canale usando il ConfigureChannel metodo:

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

ConfigureChannel viene passata un'istanza GrpcChannelOptions . Per altre informazioni, vedere Configurare le opzioni client.

Nota

Alcune proprietà vengono impostate su GrpcChannelOptions prima dell'esecuzione del ConfigureChannel callback:

Questi valori possono essere sottoposti a override da ConfigureChannel.

Credenziali di chiamata

È possibile aggiungere un'intestazione di autenticazione alle chiamate gRPC usando il AddCallCredentials metodo:

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

Per altre informazioni sulla configurazione delle credenziali delle chiamate, vedere Token di connessione con gRPC client factory.

Propagazione della scadenza e dell'annullamento

I client gRPC creati dalla factory in un servizio gRPC possono essere configurati con EnableCallContextPropagation() per propagare automaticamente la scadenza e il token di annullamento alle chiamate figlio. Il EnableCallContextPropagation() metodo di estensione è disponibile nel pacchetto NuGet Grpc.AspNetCore.Server.ClientFactory .

La propagazione del contesto di chiamata funziona leggendo la scadenza e il token di annullamento dal contesto di richiesta gRPC corrente e propagandoli automaticamente alle chiamate in uscita effettuate dal client gRPC. La propagazione del contesto delle chiamate è un ottimo modo per garantire che gli scenari gRPC complessi annidati propagano sempre la scadenza e l'annullamento.

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

Per impostazione predefinita, EnableCallContextPropagation genera un errore se il client viene usato all'esterno del contesto di una chiamata gRPC. L'errore è progettato per avvisare che non esiste un contesto di chiamata da propagare. Se si vuole usare il client all'esterno di un contesto di chiamata, eliminare l'errore quando il client è configurato con SuppressContextNotFoundErrors:

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

Per altre informazioni sulle scadenze e l'annullamento RPC, vedere Reliable gRPC services with deadline and cancellation( Reliable gRPC services with deadline and cancellation).

Client denominati

In genere, un tipo di client gRPC viene registrato una volta e quindi inserito direttamente nel costruttore di un tipo da DI. Tuttavia, esistono scenari in cui è utile disporre di più configurazioni per un client. Ad esempio, un client che effettua chiamate gRPC con e senza autenticazione.

Più client con lo stesso tipo possono essere registrati assegnando a ogni client un nome. Ogni client denominato può avere una propria configurazione. Il metodo di estensione generico AddGrpcClient include un overload che include un parametro name:

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

Il codice precedente:

  • Registra il GreeterClient tipo due volte, specificando un nome univoco per ogni oggetto.
  • Configura impostazioni diverse per ogni client denominato. La GreeterAuthenticated registrazione aggiunge le credenziali al canale in modo che le chiamate gRPC effettuate con esso siano autenticate.

Un client gRPC denominato viene creato nel codice dell'app usando GrpcClientFactory. Il tipo e il nome del client desiderato viene specificato usando il metodo generico GrpcClientFactory.CreateClient :

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

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

Risorse aggiuntive

L'integrazione di gRPC con HttpClientFactory offre un modo centralizzato per creare client gRPC. Può essere usato come alternativa alla configurazione di istanze client gRPC autonome. L'integrazione della factory è disponibile nel pacchetto NuGet Grpc.Net.ClientFactory .

La factory offre i vantaggi seguenti:

  • Fornisce una posizione centrale per la configurazione di istanze client gRPC logiche
  • Gestisce la durata del sottostante HttpClientMessageHandler
  • Propagazione automatica della scadenza e dell'annullamento in un servizio gRPC ASP.NET Core

Registrare client gRPC

Per registrare un client gRPC, è possibile usare il metodo di estensione generico AddGrpcClient all'interno di , specificando la classe client tipizzata gRPC e l'indirizzo Startup.ConfigureServicesdel servizio:

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

Il tipo di client gRPC viene registrato come temporaneo con inserimento di dipendenze (DI). Il client può ora essere inserito e usato direttamente nei tipi creati da DI. ASP.NET Core controller MVC, SignalR hub e servizi gRPC sono posti in cui i client gRPC possono essere inseriti automaticamente:

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

Configurare HttpHandler

HttpClientFactory crea l'oggetto HttpMessageHandler usato dal client gRPC. I metodi standard HttpClientFactory possono essere usati per aggiungere middleware delle richieste in uscita o per configurare il sottostante HttpClientHandler di HttpClient:

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

Per altre informazioni, vedere Effettuare richieste HTTP usando IHttpClientFactory.

Configurare gli intercettori

Gli intercettori gRPC possono essere aggiunti ai client usando il AddInterceptor metodo .

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

Il codice precedente:

  • Registra il GreeterClient tipo.
  • Configura un LoggingInterceptor oggetto per questo client. LoggingInterceptor viene creato una volta e condiviso tra GreeterClient istanze.

Per impostazione predefinita, un intercettatore viene creato una volta e condiviso tra i client. Questo comportamento può essere sottoposto a override specificando un ambito durante la registrazione di un intercettatore. La factory client può essere configurata per creare un nuovo intercettatore per ogni client specificando InterceptorScope.Client.

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

La creazione di intercettori con ambito client è utile quando un intercettatore richiede servizi con ambito o temporanei da DI.

È possibile usare un intercettatore gRPC o le credenziali del canale per inviare Authorization metadati a ogni richiesta. Per altre informazioni sulla configurazione dell'autenticazione, vedere Inviare un token di connessione con gRPC client factory.

Configurare il canale

È possibile applicare una configurazione aggiuntiva a un canale usando il ConfigureChannel metodo:

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

ConfigureChannel viene passata un'istanza GrpcChannelOptions . Per altre informazioni, vedere Configurare le opzioni client.

Nota

Alcune proprietà vengono impostate su GrpcChannelOptions prima dell'esecuzione del ConfigureChannel callback:

Questi valori possono essere sottoposti a override da ConfigureChannel.

Propagazione della scadenza e dell'annullamento

I client gRPC creati dalla factory in un servizio gRPC possono essere configurati con EnableCallContextPropagation() per propagare automaticamente la scadenza e il token di annullamento alle chiamate figlio. Il EnableCallContextPropagation() metodo di estensione è disponibile nel pacchetto NuGet Grpc.AspNetCore.Server.ClientFactory .

La propagazione del contesto di chiamata funziona leggendo la scadenza e il token di annullamento dal contesto di richiesta gRPC corrente e propagandoli automaticamente alle chiamate in uscita effettuate dal client gRPC. La propagazione del contesto delle chiamate è un ottimo modo per garantire che gli scenari gRPC complessi annidati propagano sempre la scadenza e l'annullamento.

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

Per impostazione predefinita, EnableCallContextPropagation genera un errore se il client viene usato all'esterno del contesto di una chiamata gRPC. L'errore è progettato per avvisare che non esiste un contesto di chiamata da propagare. Se si vuole usare il client all'esterno di un contesto di chiamata, eliminare l'errore quando il client è configurato con SuppressContextNotFoundErrors:

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

Per altre informazioni sulle scadenze e l'annullamento RPC, vedere Reliable gRPC services with deadline and cancellation( Reliable gRPC services with deadline and cancellation).

Client denominati

In genere, un tipo di client gRPC viene registrato una volta e quindi inserito direttamente nel costruttore di un tipo da DI. Tuttavia, esistono scenari in cui è utile disporre di più configurazioni per un client. Ad esempio, un client che effettua chiamate gRPC con e senza autenticazione.

Più client con lo stesso tipo possono essere registrati assegnando a ogni client un nome. Ogni client denominato può avere una propria configurazione. Il metodo di estensione generico AddGrpcClient include un overload che include un parametro name:

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

Il codice precedente:

  • Registra il GreeterClient tipo due volte, specificando un nome univoco per ogni oggetto.
  • Configura impostazioni diverse per ogni client denominato. La GreeterAuthenticated registrazione aggiunge le credenziali al canale in modo che le chiamate gRPC effettuate con esso siano autenticate.

Un client gRPC denominato viene creato nel codice dell'app usando GrpcClientFactory. Il tipo e il nome del client desiderato viene specificato usando il metodo generico GrpcClientFactory.CreateClient :

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

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

Risorse aggiuntive