Integración de la fábrica de cliente de gRPC en .NET

Por James Newton-King

La integración de gRPC con HttpClientFactory ofrece una manera centralizada de crear clientes gRPC. Se puede usar como alternativa a la configuración de instancias de cliente de gRPC independientes. La integración de fábrica está disponible en el paquete NuGet Grpc.Net.ClientFactory.

La fábrica ofrece las ventajas siguientes:

  • Proporciona una ubicación central para configurar instancias de cliente de gRPC.
  • Administra la duración del objeto HttpClientMessageHandler subyacente.
  • Propagación automática de la fecha límite y la cancelación en un servicio gRPC de ASP.NET Core

Registro de clientes gRPC

Para registrar un cliente gRPC, se puede usar el método de extensión genérico AddGrpcClient dentro de una instancia de WebApplicationBuilder en el punto de entrada de la aplicación de Program.cs, especificando la clase del cliente con tipo de gRPC y la dirección del servicio:

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

El tipo de cliente gRPC se registra como transitorio con la inserción de dependencias (DI). Ahora el cliente se puede insertar y consumir directamente en los tipos creados por inserción de dependencias. Los controladores de ASP.NET Core MVC, los concentradores de SignalR y los servicios gRPC son lugares en los que se pueden insertar clientes gRPC de forma automática:

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

Configuración de HttpHandler

HttpClientFactory crea el objeto HttpMessageHandler que usa el cliente gRPC. Se pueden usar métodos HttpClientFactory estándar para agregar middleware de solicitud de salida o para configurar el objeto HttpClientHandler subyacente de 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;
    });

Para obtener más información, vea Realización de solicitudes HTTP con IHttpClientFactory.

Configuración de interceptores

Los interceptores gRPC se pueden agregar a los clientes usando el método AddInterceptor.

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

El código anterior:

  • Registra el tipo GreeterClient.
  • Configura un LoggingInterceptor para este cliente. LoggingInterceptor se crea una vez y se comparte entre las instancias de GreeterClient.

De forma predeterminada, se crea un interceptor una vez y se comparte entre los clientes. Este comportamiento se puede invalidar especificando un ámbito cuando se registra un interceptor. El generador de clientes se puede configurar para crear un nuevo interceptor para cada cliente especificando InterceptorScope.Client.

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

La creación de interceptores con ámbito de cliente es útil cuando un interceptor requiere servicios con ámbito o transitorios desde la inserción de dependencias.

Un interceptor de gRPC o credenciales de canal pueden usarse para enviar metadatos Authorization con cada solicitud. Para obtener más información sobre cómo configurar la autenticación, consulte la sección sobre cómo enviar un token de portador con la fábrica de cliente de gRPC.

Configuración del canal

Se puede aplicar una configuración adicional a un canal con el método ConfigureChannel:

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

A ConfigureChannel se le pasa una instancia de GrpcChannelOptions. Para obtener más información, vea Configuración de opciones de cliente.

Nota:

Algunas propiedades se establecen en GrpcChannelOptions antes de ejecutar la devolución de llamada ConfigureChannel:

Estos valores se pueden invalidar con ConfigureChannel.

Credenciales de llamada

Se puede agregar un encabezado de autenticación a las llamadas de gRPC mediante el método AddCallCredentials:

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

Para obtener más información sobre cómo configurar las credenciales de llamada, consulte la sección sobre cómo enviar un token de portador con la fábrica de cliente de gRPC.

Propagación de la fecha límite y la cancelación

Los clientes gRPC creados por la fábrica en un servicio gRPC se pueden configurar con EnableCallContextPropagation() para propagar automáticamente la fecha límite y el token de cancelación a las llamadas secundarias. El método de extensión EnableCallContextPropagation() está disponible en el paquete NuGet Grpc.AspNetCore.Server.ClientFactory.

La propagación del contexto de llamada funciona mediante la lectura de la fecha límite y el token de cancelación del contexto de solicitud gRPC actual y su propagación automática a las llamadas salientes realizadas por el cliente gRPC. La propagación del contexto de llamada es una excelente manera de garantizar que los escenarios complejos de gRPC anidados siempre propagan la fecha límite y la cancelación.

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

De forma predeterminada, EnableCallContextPropagation genera un error si el cliente se usa fuera del contexto de una llamada a gRPC. El error se ha diseñado para avisarle de que no hay un contexto de llamada que propagar. Si desea utilizar el cliente fuera de un contexto de llamada, suprima el error cuando el cliente esté configurado con SuppressContextNotFoundErrors:

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

Para más información sobre las fechas límite y la cancelación de RPC, vea Servicios gRPC confiables con fechas límite y cancelación.

Clientes con nombre

Normalmente, un tipo de cliente gRPC se registra una vez y, a continuación, se inserta directamente en el constructor de un tipo mediante DI. Sin embargo, hay escenarios en los que resulta útil tener varias configuraciones para un cliente. Por ejemplo, un cliente que realiza llamadas gRPC con y sin autenticación.

Para registrar varios clientes con el mismo tipo, asigne un nombre a cada cliente. Cada cliente con nombre puede tener su propia configuración. El método de extensión AddGrpcClient genérico tiene una sobrecarga que incluye un parámetro de nombre:

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

El código anterior:

  • Registra el tipo GreeterClient dos veces, especificando un nombre único cada vez.
  • Configura diferentes opciones para cada cliente con nombre. El registro GreeterAuthenticated agrega credenciales al canal para que se autentiquen las llamadas gRPC realizadas con él.

Se crea un cliente gRPC con nombre en el código de la aplicación mediante GrpcClientFactory. El tipo y el nombre del cliente deseado se especifican mediante el método GrpcClientFactory.CreateClient genérico:

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

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

Recursos adicionales

La integración de gRPC con HttpClientFactory ofrece una manera centralizada de crear clientes gRPC. Se puede usar como alternativa a la configuración de instancias de cliente de gRPC independientes. La integración de fábrica está disponible en el paquete NuGet Grpc.Net.ClientFactory.

La fábrica ofrece las ventajas siguientes:

  • Proporciona una ubicación central para configurar instancias de cliente de gRPC.
  • Administra la duración del objeto HttpClientMessageHandler subyacente.
  • Propagación automática de la fecha límite y la cancelación en un servicio gRPC de ASP.NET Core

Registro de clientes gRPC

Para registrar un cliente gRPC, se puede usar el método de extensión genérico AddGrpcClient dentro de Startup.ConfigureServices, y especificar la clase del cliente con tipo de gRPC y la dirección del servicio:

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

El tipo de cliente gRPC se registra como transitorio con la inserción de dependencias (DI). Ahora el cliente se puede insertar y consumir directamente en los tipos creados por inserción de dependencias. Los controladores de ASP.NET Core MVC, los concentradores de SignalR y los servicios gRPC son lugares en los que se pueden insertar clientes gRPC de forma automática:

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

Configuración de HttpHandler

HttpClientFactory crea el objeto HttpMessageHandler que usa el cliente gRPC. Se pueden usar métodos HttpClientFactory estándar para agregar middleware de solicitud de salida o para configurar el objeto HttpClientHandler subyacente de HttpClient:

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

Para obtener más información, vea Realización de solicitudes HTTP con IHttpClientFactory.

Configuración de interceptores

Los interceptores gRPC se pueden agregar a los clientes usando el método AddInterceptor.

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

El código anterior:

  • Registra el tipo GreeterClient.
  • Configura un LoggingInterceptor para este cliente. LoggingInterceptor se crea una vez y se comparte entre las instancias de GreeterClient.

De forma predeterminada, se crea un interceptor una vez y se comparte entre los clientes. Este comportamiento se puede invalidar especificando un ámbito cuando se registra un interceptor. El generador de clientes se puede configurar para crear un nuevo interceptor para cada cliente especificando InterceptorScope.Client.

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

La creación de interceptores con ámbito de cliente es útil cuando un interceptor requiere servicios con ámbito o transitorios desde la inserción de dependencias.

Un interceptor de gRPC o credenciales de canal pueden usarse para enviar metadatos Authorization con cada solicitud. Para obtener más información sobre cómo configurar la autenticación, consulte la sección sobre cómo enviar un token de portador con la fábrica de cliente de gRPC.

Configuración del canal

Se puede aplicar una configuración adicional a un canal con el método ConfigureChannel:

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

A ConfigureChannel se le pasa una instancia de GrpcChannelOptions. Para obtener más información, vea Configuración de opciones de cliente.

Nota:

Algunas propiedades se establecen en GrpcChannelOptions antes de ejecutar la devolución de llamada ConfigureChannel:

Estos valores se pueden invalidar con ConfigureChannel.

Propagación de la fecha límite y la cancelación

Los clientes gRPC creados por la fábrica en un servicio gRPC se pueden configurar con EnableCallContextPropagation() para propagar automáticamente la fecha límite y el token de cancelación a las llamadas secundarias. El método de extensión EnableCallContextPropagation() está disponible en el paquete NuGet Grpc.AspNetCore.Server.ClientFactory.

La propagación del contexto de llamada funciona mediante la lectura de la fecha límite y el token de cancelación del contexto de solicitud gRPC actual y su propagación automática a las llamadas salientes realizadas por el cliente gRPC. La propagación del contexto de llamada es una excelente manera de garantizar que los escenarios complejos de gRPC anidados siempre propagan la fecha límite y la cancelación.

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

De forma predeterminada, EnableCallContextPropagation genera un error si el cliente se usa fuera del contexto de una llamada a gRPC. El error se ha diseñado para avisarle de que no hay un contexto de llamada que propagar. Si desea utilizar el cliente fuera de un contexto de llamada, suprima el error cuando el cliente esté configurado con SuppressContextNotFoundErrors:

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

Para más información sobre las fechas límite y la cancelación de RPC, vea Servicios gRPC confiables con fechas límite y cancelación.

Clientes con nombre

Normalmente, un tipo de cliente gRPC se registra una vez y, a continuación, se inserta directamente en el constructor de un tipo mediante DI. Sin embargo, hay escenarios en los que resulta útil tener varias configuraciones para un cliente. Por ejemplo, un cliente que realiza llamadas gRPC con y sin autenticación.

Para registrar varios clientes con el mismo tipo, asigne un nombre a cada cliente. Cada cliente con nombre puede tener su propia configuración. El método de extensión AddGrpcClient genérico tiene una sobrecarga que incluye un parámetro de nombre:

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

El código anterior:

  • Registra el tipo GreeterClient dos veces, especificando un nombre único cada vez.
  • Configura diferentes opciones para cada cliente con nombre. El registro GreeterAuthenticated agrega credenciales al canal para que se autentiquen las llamadas gRPC realizadas con él.

Se crea un cliente gRPC con nombre en el código de la aplicación mediante GrpcClientFactory. El tipo y el nombre del cliente deseado se especifican mediante el método GrpcClientFactory.CreateClient genérico:

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

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

Recursos adicionales