gRPC & Dotnet por dónde empezar?

gRPC es un mecanismo moderno de invocación de métodos remotos (RPC) entre procesos/sistemas/servicios, desarrollado por Google. Utiliza como transporte HTTP/2 y Protocol Buffers como lenguaje de descripción de interfaz

¿Cuáles son sus características?

El transporte se realiza sobre HTTP/S. Hace uso de Protobuff como interfaz de descripción de los mensajes. Protobuff es neutral y se usa para definir los mensajes y recibidos por un servicio gRPC. Proporciona autenticación, streaming bidireccional y control de flujo. Permite generar clientes multiplataformas y su uso más común es entre microservicios.

¿Cuáles son sus fortalezas?

¿Cuáles son sus debilidades?

Hoy su mayor debilidad es que hay un soporte limitado en los navegadores. Los navegadores no poseen el nivel de control necesario para solicitudes que un cliente gRPC requiere. No hay forma de forzar el uso de HTTP/2 y las tramas HTTP/2 son inaccesibles al navegador.

Por donde empezamos

Lo primero que tenemos que tener en cuenta es definir el contrato y los mensajes que se va a intercambiar entre el cliente y el servidor gRPC. Toda esta definición se realizara en un fichero que tiene una extensión .proto. Por ejemplo, si queremos crear un servicio de Avengers por ejemplo puede tener la siguiente nomenclatura

syntax = "proto3";
import "google/protobuf/empty.proto";
option csharp_namespace = "GrpcAvenger";

service Avenger {
  rpc GetAll (google.protobuf.Empty) returns (GetAllReply);
  rpc Get (IdAvenger) returns (GetReply);
}
message IdAvenger {
  string name = 1;
}

message AvengerCharacter {
    string id = 1;
    string name = 2;
    string photo = 3;   
}

message GetAllReply {    
  repeated AvengerCharacter name  = 1;
}

message GetReply {
  AvengerCharacter message = 1;
}

Del fichero tengamos en cuenta un par de consideraciones en primer lugar hay una serie de tipos de Protobuf que tienen su equivalente en C#. Podemos consultarlo en el siguiente link. Si os dais cuenta cuando estamos pasando un método en blanco el objeto de Google Empty. Posteriormente para poder hacer un array de C# hay que usar la palabra repetead. Luego para crear una clase podemos decir que es similar al contenido que tenemos en el message. Si queréis alguna guía de estilo de cómo se deben definir podéis consultar el siguiente enlace.

Creando el Server

Una vez ya tenemos definido el Proto, vamos a crear nuestro servidor para ellos nos crearemos un proyecto ASP.NET Core vacío. Se que existen plantillas que ya te crean todo lo necesario para empezar a usar gRPC, sin embargo, para una primera toma de contacto con la tecnología prefiero que entendamos todo lo que hay y dejemos la magia que hace el template para más adelante.

dotnet new web

Una vez el proyecto esta creado, añadimos los siguientes paquetes de Nuget

 dotnet add package Google.Protobuf
 dotnet add package Grpc.Tools
 dotnet add package Grpc.AspNetCore

De los nugets, por un lado, tenemos el Google.Protof que nos indica los tipos que se van a usar para los tipos del proto. El Grpc.Tool es el encargado de generar la implementación del servicio base de nuestro proto sin tirar una línea de código. Y por último el paquete Grpc.AspNetcore tiene el middleware que le inyectaremos para arrancar nuestro proyecto.

Implementaremos el Servicio

Una vez ya tenemos listo nuestro proyecto para empezar a añadir la funcionalidad. En primer lugar, implementaremos el servicio. Para ello nos crearemos una carpeta “Services”, nos creamos un fichero AvengerService.cs con el siguiente contenido.

using Grpc.Core;
using GrpcAvenger;

namespace gRPCAvengers.Services
{
    public class AvengerService : Avenger.AvengerBase
    {
        private readonly ILogger<AvengerService> _logger;
        private readonly IList<AvengerCharacter> _avengersCollection;

        public AvengerService(ILogger<AvengerService> logger)
        {
            this._logger=logger;
            _avengersCollection = new List<AvengerCharacter>()
                {
                    new AvengerCharacter { Id ="1", Name="Hulk", Photo=""},
                    new AvengerCharacter { Id ="2", Name="Capitan Americana", Photo=""},
                    new AvengerCharacter { Id ="3", Name="Iron Man", Photo=""}
                 };
        }

        public override Task<GetAllReply> GetAll(Google.Protobuf.WellKnownTypes.Empty request,ServerCallContext context)
        {
            _logger.LogInformation("Init GetAll");
            var response = new GetAllReply();                        
            response.Name.AddRange(_avengersCollection);
            return Task.FromResult(response);
        }

        public override Task<GetReply> Get (IdAvenger request, ServerCallContext context)
        {
            _logger.LogInformation("Init GetAll");
            var response = new GetReply
            {
                Message= _avengersCollection.Where(x => x.Id.Equals(request.Name)).FirstOrDefault()
            };
            return Task.FromResult(response);
        }
    }
}

Configurando el arranque

En el fichero Program.cs añadiremos la linea 4 y 8 del siguiente arranque.

using gRPCAvengers.Services;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();

app.MapGet("/", () => "Hello Avengers Dev!!");
app.MapGrpcService<AvengerService>();
app.Run();

Creando el Cliente

Para la creación del cliente el proceso es muy similar al del Server. En este caso vamos a crear una aplicación de consola, aunque se puede crear cualquier tipo de proyecto.

dotnet new console

Añadiremos los siguientes paquetes Nugets, los dos primeros son exactamente igual que en el servidor y la única difencia es el tercer paquete es que será el encargado de crear el cliente gRPC con los métodos que hemos importado anteriormente.

 dotnet add package Google.Protobuf
 dotnet add package Grpc.Tools
 dotnet add package Grpc.Net.Client

Una vez ya tenemos listo vamos a crear el código, no tiene mucho misterio lo que vamos a hacer es por un lado crearnos el channel donde tenemos alojado el Servidor gRPC, en este caso lo único que tenemos que establecer es la url donde esta nuestro servicio. Con ese channel podemos crear un cliente de nuestro cliente, que de esto se encarga el paquete Nuget Grpc.Net.Client. En nuestro caso hemos invocado al método que nos devuelve todos los Avengers. Si os dais cuenta nosotros no hemos implementado un método asíncrono ni síncrono, el propio Grpc.Tools es el que se encarga de crearlo. El ejemplo quedaría de la siguiente forma:

using Grpc.Net.Client;
using GrpcAvenger;

using var channel = GrpcChannel.ForAddress("miip");
var client = new Avenger.AvengerClient(channel);
var empty = new Google.Protobuf.WellKnownTypes.Empty();
var reply = await client.GetAllAsync(empty);

foreach (var item in reply.Name)
{
    Console.WriteLine($"{item.Id}-{item.Name}-{item.Photo}");
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

Resumiendo

Nos queda pendiente muchas cosas, el testing de esta aplicación, el ponerla en una aplicación más seria, que diferencias tenemos respecto a una API Rest tradicional, casos de usos, todo esto lo abordaremos en siguientes posts. Podéis ver el ejemplo en mi repositorio de Github

Happy codding