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?
- Marco de RPC moderno, ligero y de alto rendimiento.
- Uso reducido de red con serialización binaria Protobuf. Además de multiplexación de varias llamadas sobre una única conexión TCP. Esto para lo que hemos sufrido la falta de sockets disponibles en una WebApp es una bendición.
- Todos los frameworks para gRPC proveen soporte para generación de código. Esto evita que creemos librerías para consumir dichos servicios y podamos ahorrar tiempo de ejecución.
- Soporte para streaming a través de HTTP/S
- gRPC permite a los clientes especificar cuánto están dispuesto para que se complete la solicitud, el tiempo límite se envía al servidor y este decide que acción tomar si excede el tiempo.
- Seguridad del envió de los mensajes, a diferencia de REST los mensajes gGRPC no son legibles por humanos.
¿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