NET5: Introducción a gRPC para implementar servicios en C# y llamarlo con BloomRPC

Publicado por Santi Macias el

Microsoft.NET5Visual StudioDesarrollo AplicacionesgRPCBloomRPC

En el artículo de hoy, continuamos viendo novedades en .NET5 y que proyectos podemos crear con Visual Studio 2019, en concreto vamos a explicar cómo trabajar con servicios gRPC y comunicarnos con ellos.

A lo largo de estos años han aparecido un gran número de alternativas para conectar y comunicar las aplicaciones y servicios por parte de empresas y organismos oficiales como DCOM, CORBA, RPC, JAVA RMI, SOAP y REST que hoy en día encontramos en distintos tipos de proyectos y aplicaciones legacy, monolíticas, distribuidas, microservicios, etc.

La arquitectura, desarrollo y tecnologías de software han evolucionado muchísimo en los últimos años, y por lo que parece no tiene pinta de cambiar, dentro de todo este proceso de evolución la parte de comunicar las aplicaciones siempre ha sido motivo de interesantes discusiones y donde contamos con otra tecnología para nuestros proyectos llamada gRPC.

Introducción a gRPC

gRPC, es un framework open-source de alto rendimiento creado por Google que permite la comunicación RPC entre servicios mediante conexiones HTTP/2, utiliza la serialización binaria y Protocol Buffers como lenguaje de descripción de interfaz que explicaremos en este articulo.

Para tener una visión general, gRPC es un proyecto de código fuente abierto que empresas como Google, Netflix o Slack, utilizan internamente para conectar microservicios y cada vez más se está adoptando en muchos proyectos tanto de comunidad, como privados, sobre todo relacionados con Kubernetes y microservicios.

Los escenarios más comunes donde puede ser interesante utilizar gRPC suelen ser:

  • Microservicios
  • Comunicación punto a punto
  • Comunicación entre procesos y sockets
  • Entornos restringidos de red o baja latencia
  • Streaming unidireccional y bidireccional

Para diferenciarlo de los servicios como SOAP con XML o REST con JSON, hay que destacar que gRPC soporta la autenticación, el intercambio de datos en modo streaming, tanto unidireccional como bidireccionalmente, control de flujo y serialización binaria mucho más eficiente que utilizar serialización de texto con JSON o XML.

Otro aspecto importante es que gRPC forma parte de la CNCF - Cloud Native Computing Foundation como incubation project, toda la información al respecto la podéis consultar aquí: https://www.grpc.io

Como funciona gRPC

El concepto de cómo funciona, sin entran en detalles muy técnicos, es mediante el uso de la tecnología RPC-Remote Procedure Call, imaginar una aplicación cliente que puede llamar a métodos de otra aplicación servidora que esta físicamente situada en otro equipo y pueda tratase como un objeto local. Para que este concepto funcione, tanto cliente como servidor deben implementar una misma interfaz que actúe de común denominador entre las dos partes.

Este parte común es un contrato que ambas partes acuerdan de antemano para poder comunicarse entre sí y que tiene soporte para una gran cantidad de lenguajes de programación como Java, Node, Go, Python,… y entre ellos podemos utilizar C# con .NET5, aclarando para los que lleváis tiempo programando que también existen librerías de Nuget para utilizar gRPC con .NET/Core.

Para especificar el contrato común tenemos Protocol buffers, un lenguaje de tipo IDL (Interface Description Language) creado también por Google cuyo objetivo es definir un contrato entre servidor y cliente que debemos implementar antes de comenzar a crear el servicio gRPC donde el uso de mensajes son el objeto de transferencia de datos principal.

Los Protocol Buffers son archivos que tienen extensión '.proto' y  describen un API para intercambio de datos usando un "idioma propio" y agnósticos a la plataforma o lenguaje de programación donde se van a usar lo cual nos da mucha flexibilidad como veremos en el ejemplo.

Una duda muy común, que es importante que sepáis y es una ventaja de usar Protocol Buffers es que se pueden crear servicios de API REST con JSON automáticamente desde servicios gRPC, anotando para ello el archivo .proto con metadatos HTTP, esto permite que una aplicación soporte tanto REST como gRPC, sin duplicar el esfuerzo de tener que crear servicios independientes para cada caso.

Toda la información relacionada con Protocol Buffers la podrás encontrar en el sitio de desarrolladores de Google en este enlace: https://developers.google.com/protocol-buffers/docs/proto3

Incompatibilidades en ciertos escenarios web y Microsoft

Antes de empezar a programar servicios de gRPC, leyendo la documentación oficial de Microsoft nos advierte de ciertas incompatibilidades que debemos tener en cuenta para el desarrollo de los proyectos tanto con .NET Core como .NET5.

Por un lado, hay problemas en llamar directamente a un servicio gRPC desde los exploradores, debido a algunas características de HTTP/2 con los navegadores no proporciona el nivel de control requerido a través de solicitudes web para admitir un cliente de gRPC.

Para estos escenarios web, disponemos del proyecto gRPC-Web que facilita y proporciona compatibilidad para los browsers y esta soportado en .NET5 y .NET Core.

También es posible utilizar Envoy como reverse proxy que tiene soporte para HTTP 1.1, HTTP/2 y gRPC aunque todo esto queda fuera del alcance del articulo.

Si queréis profundizar más: Use gRPC in browser apps | Microsoft Docs

Por otro lado, hasta la fecha la programación gRPC con ASP.NET Core no es compatible con los servicios de Azure App Service, ni con Internet Information Server (IIS) debido a la implementación HTTP/2 de Http.Sys por lo que tenerlo en cuenta en vuestros escenarios de producción.

Toda la información sobre estas incompatibilidades se puede consultar aquí: https://github.com/dotnet/AspNetCore/issues/9020

Implementando gRPC en NET5

La mejor forma de entender realmente gRPC es verlo con un ejemplo para mostrar la estructura del proyecto, el protocolo y como se tienen que diseñar este tipo de servicios.

Si viste los artículos anteriores sobre .NET5 y la instalación de Visual Studio 2019 ya tienes configurado todo lo necesario para desarrollar y trabajar con gRPC dentro del ecosistema Microsoft.

El nuevo framework .NET5 tiene un soporte nativo para gRPC y los archivos Protocol Buffers podemos comprobarlo rápidamente si ejecutamos la línea de comandos de dotnet con “dotnet new” como se muestra en la imagen.

En nuestro caso, vamos a utilizar Visual Studio 2019 y la plantilla para crear un servicio gRPC donde estudiar como implementarlo de forma fácil y didáctica.

Abrimos Visual Studio, creamos un nuevo proyecto, buscamos la plantilla de tipo “grpc” y pulsamos crear, seguidamente indicaremos el nombre de “PruebaGrpcService”.

El siguiente paso, seleccionamos el framework de .NET 5.0 y la plantilla de ASP.NET Core gRPC Service, comentar que está plantilla viene configurada con ciertas caracteristicas de http2 por defecto para llamar al servicio.

Por último, pulsamos el botón crear y nos aparecerá el proyecto ya preparado en el explorador de soluciones.

Ya tenemos listo un ejemplo gRPC, lo siguiente es revisar la estructura del proyecto para explicar sus partes principales.

Revisando la estructura y código fuente del proyecto

Si expandimos la solución, podemos observar la composición de la plantilla y las partes principales de la misma que como observamos es la típica plantilla de ASP.NET Core con sus paquetes Nuget y dos carpetas llamadas Services y Protos donde encontramos la definición del servicio y el archivo Protocol Buffers que hablamos inicialmente en la introducción.

Si abrimos la clase Programs.cs veremos el punto de entrada de la aplicación y la creacion del WebHost.

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        // Additional configuration is required to successfully run gRPC on macOS.
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

Dentro del archivo Startup.cs tenemos la configuración de los servicios de gRPC y la definición de los endpoints.

    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<GreeterService>();

                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
                });
            });
        }
    }

En el archivo appsettings.json se muestra la configuración de la aplicación, aquí se indica que se usará el servidor Kestrel con protocolo http2 y no se hace mención de IIS para nada.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http2"
    }
  }
}

En el archivo greet.proto se muestra el contrato con la definición del servicio mediante Protocol Buffers que apuntan al servicio PruebaGrpcService como podéis ver y se indican los métodos y los mensajes para la comunicación entre cliente y servidor.

syntax = "proto3";

option csharp_namespace = "PruebaGrpcService";

package greet;

// The greeting service definition.
service Greeter {  
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {  
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {  
  string message = 1;
}

En el archivo GreeterService.cs se encuentra la implementación del servicio con el método de SayHello, la idea del ejemplo es entender la estructura y la forma de implementarlo según la definición del contrato que se encuentra en el archivo  greet.proto

    public class GreeterService : Greeter.GreeterBase
    {
        private readonly ILogger<GreeterService> _logger;
        public GreeterService(ILogger<GreeterService> logger)
        {
            _logger = logger;
        }

        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloReply
            {
                Message = "Hello " + request.Name
            });
        }
    }

Por último, en el propio PruebaGrpcService.csproj podemos ver como se integra todo esto dentro de la estructura XML del proyecto de ASP.NET Core 5.0

<Project Sdk="Microsoft.NET.Sdk.Web">  
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.32.0" />
  </ItemGroup>
</Project>  

Ahora solo nos falta probar el servicio y saber como realizar peticiones gRPC, para ello, lo ejecutamos para dejarlo levantado escuchando peticiones desde Visual Studio.

Llamando al servicio gRPC con BloomRPC

Para realizar una llamada a nuestro servicio, vamos a utilizar una herramienta como BloomRPC para consultar el servicio PruebaGrpcService que hemos levantado en Visual Studio.

BloomRPC es open-source, funciona en MacOS, Linux, Windows y ofrece todas estas características:

  • Native GRPC calls
  • Unary Calls and Server Side Streaming Support
  • Client side and Bi-directional Streaming
  • Automatic Input recognition
  • Multi tabs operations
  • Metadata support
  • Persistent Workspace
  • Request Cancellation

La podéis instalar desde GitHub: Bloomrpc - GUI Client for GRPC Services

Una vez instalada, abriremos la interfaz de BloomRPC para llamar al servicio gRPC, por un lado tenemos que cargar el archivo greet.proto con la definición del servicio que tenemos en el código fuente y consultar en el botón de View Proto la descripción del archivo que hemos cargado.

Para invocar al servicio gRPC tenemos que indicar el endpoint, en nuestro proyecto tenemos configurado localhost:5000 o localhost:5001 que es donde esta escuchando peticiones, esto se puede consultar en la consola del servicio que hemos ejecutado desde Visual Studio.

Realizamos la llamada a localhost:5000 y vemos que responde correctamente.

Ahora, realizamos otra llamada a localhost:5001 y vemos que responde un error “UNKNOWN: Stream removed" esto es un problema conocido en BloomRPC por temas del servidor Kestrel que está documentado como arreglarlo aquí: https://github.com/uw-labs/bloomrpc/issues/138

Para solucionarlo, cambiamos en el código fuente del servicio gRPC en el archivo Program.cs con lo siguiente.

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        // Additional configuration is required to successfully run gRPC on macOS.
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseKestrel(options =>
                    {
                        options.Listen(IPAddress.Any, 5001, listenOptions =>
                        {
                            listenOptions.Protocols = HttpProtocols.Http2;
                        });
                    });

                    webBuilder.UseStartup<Startup>();
                });
    }

Con este cambio en nuestro código fuente, volvemos a probar y veremos  que ya responde correctamente la petición a localhost:5001

Conclusiones

Este articulo es una introducción a gRPC donde hemos visto como implementar un servicio simple en C#, el uso de Protocols Buffers para definir el contrato y como llamar al servicio con BloomRPC.

Antes de adoptarlo en los proyectos, hay que valorar muy bien en que escenarios nos puede interesar utilizar gRPC, por ejemplo, en aquellos donde necesitamos un alto rendimiento y también tener en cuenta la complejidad y ciertas incompatibilidades que nos podemos encontrar en escenarios de producción.

Si te ha gustado el artículo, ¡Síguenos en Twitter y no te pierdas los próximos posts de .NET5!

Autor

Santi Macias

Microsoft Tech Lead en knowmad mood, +20 años trabajando con tecnologías Microsoft actualmente centrado sobretodo en Azure, Cloud Native, DevOps, Docker, Kubernetes, Microservicios y Serverless.