Integrazione della factory client 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 ASP.NET Core gRPC.

Registrare i client gRPC

Per registrare un client gRPC, il metodo di estensione generico AddGrpcClient può essere usato all'interno di un'istanza di WebApplicationBuilder nel punto di 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 delle dipendenze . Il client può ora essere inserito e utilizzato direttamente nei tipi creati dall'inserimento delle dipendenze. ASP.NET controller MVC core, SignalR hub e servizi gRPC sono posizioni 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 il middleware delle richieste in uscita o per configurare l'oggetto 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 con IHttpClientFactory.

Configurare gli intercettori

È possibile aggiungere intercettori gRPC 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 sola volta e condiviso tra GreeterClient le istanze.

Per impostazione predefinita, un intercettore viene creato una sola volta e condiviso tra i client. Questo comportamento può essere sottoposto a override specificando un ambito durante la registrazione di un intercettore. La factory client può essere configurata per creare un nuovo intercettore 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 intercettore richiede servizi con ambito o temporanei dall'inserimento di dipendenze.

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

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 di . Per altre informazioni, vedere Configurare le opzioni client.

Nota

Alcune proprietà vengono impostate prima GrpcChannelOptions 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 di chiamata, vedere Token di connessione con la factory client gRPC.

Propagazione scadenza e 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 di chiamata è 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 l'utente 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 .For more information about deadline and RPC cancellation, see Reliable gRPC services with deadline and cancellation.

Client denominati

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

È possibile registrare più client con lo stesso tipo 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 ognuno di essi.
  • Configura impostazioni diverse per ogni client denominato. La GreeterAuthenticated registrazione aggiunge le credenziali al canale in modo che le chiamate gRPC effettuate con esso vengano autenticate.

Un client gRPC denominato viene creato nel codice dell'app usando GrpcClientFactory. Il tipo e il nome del client desiderato sono specificati 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 dell'oggetto sottostante HttpClientMessageHandler
  • Propagazione automatica della scadenza e dell'annullamento in un servizio ASP.NET Core gRPC

Registrare i client gRPC

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

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

Il tipo di client gRPC viene registrato come temporaneo con inserimento delle dipendenze . Il client può ora essere inserito e utilizzato direttamente nei tipi creati dall'inserimento delle dipendenze. ASP.NET controller MVC core, SignalR hub e servizi gRPC sono posizioni 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 il middleware delle richieste in uscita o per configurare l'oggetto 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 con IHttpClientFactory.

Configurare gli intercettori

È possibile aggiungere intercettori gRPC 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 sola volta e condiviso tra GreeterClient le istanze.

Per impostazione predefinita, un intercettore viene creato una sola volta e condiviso tra i client. Questo comportamento può essere sottoposto a override specificando un ambito durante la registrazione di un intercettore. La factory client può essere configurata per creare un nuovo intercettore 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 intercettore richiede servizi con ambito o temporanei dall'inserimento di dipendenze.

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

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 di . Per altre informazioni, vedere Configurare le opzioni client.

Nota

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

Questi valori possono essere sottoposti a override da ConfigureChannel.

Propagazione scadenza e 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 di chiamata è 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 l'utente 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 .For more information about deadline and RPC cancellation, see Reliable gRPC services with deadline and cancellation.

Client denominati

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

È possibile registrare più client con lo stesso tipo 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 ognuno di essi.
  • Configura impostazioni diverse per ogni client denominato. La GreeterAuthenticated registrazione aggiunge le credenziali al canale in modo che le chiamate gRPC effettuate con esso vengano autenticate.

Un client gRPC denominato viene creato nel codice dell'app usando GrpcClientFactory. Il tipo e il nome del client desiderato sono specificati 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