gRPC es un sofisticado framework de Apificación que permite la orquestación de microservicios de manera sencilla y eficiente desde el punto de vista del rendimiento.Se trata de un framework desarrollado y utilizado por Google y por lo tanto con alto nivel de calidad y madurez que lo está convirtiendo en un claro competidor del tradicional REST.

Basado en el modelo cliente/servidor mediante RPC ,utiliza HTTP2,es multilenguaje y se fundamenta en protocol buffer como lenguaje de serialización.

Su kernel RPC permite el uso de métodos remotos de manera local.

La adopción de HTTP2 supone una serie de ventajas que ofrecen amplios beneficios tanto en la optimización del ancho de banda como en el consumo de CPU.

  • Multiplexación de recursos. Envio de los recursos troceados mediante una única conexión en vez de utilizar múltiples conéxiones.
  • Envío automático de los recursos a la caché del navegador. Anticipación de la disponibilidad a la solicitud con la consiguiente disminución de la latencia.
  • Los headers van comprimidos.
  • A diferencia de la versión 1, el protocolo de comunicación es binario en vez de texto, teniendo una mejor interpretación, siendo más compacto para el transporte y lo que es más importante con una tasa de errores mucho menor.

Otro elemento diferenciador a tener en cuenta y que supone un beneficio en rendimiento es el uso de protocol buffer como lenguaje de serialización. Aparte de su sencillez de implementación, tiene muy optimizado el uso de caracteres, lo cual supone una reducción del tamaño (aproximadamente la mitad que un equivalente en json). Por otro lado su sintaxis es fácilmente interpretable en tiempo de compilación al ser muy parecida a binario.

Veamos un ejemplo!

Vamos a analizar un microservicio gRPC springboot con maven y mongoDb reactive. Teneis disponible el código fuente en mi github

La aplicación consiste en una transacción para agregar bloques a una cadena de bloques mediante gRPC. Para ello vamos a tener un cliente donde se generará un bloque que se enviará al servidor donde se validará su corrección y se persistirá en Mongo en caso de que el bloque sea válido.

Veamos las particularidades de gRPC en el pom.xml

  • dependencia grpc-spring-boot-starter versión 3.0.0
  • plugin protobuf-maven-plugin versión 0.6.1

Para empezar vamos a crear el archivo blockChain.proto con la estructura de datos serializable de la interfaz de comunicación. Sin profundizar demasiado (nos supondría un artículo específico) veamos en que consiste.

syntax=proto3 indica la versión de proto a utilizar. En caso de omitirse utilizará la 2.

option java_multiple_files = true y package com.atb.grpc.ninja.blockchain son opciones de compilación que indican que el lenguaje es java con estructura de clases y el nombre del paquete raíz de las clases generadas al construirse el proyecto con Maven.

A continuación tenemos la definición de los datos 

message se utiliza para determinar una estructura, en nuestro caso Block y BlockConfirm que corresponden a la request y response respectivame del servicio addBlock.

Cada sub elemento de Block se constituye de tipo , nombre y tag.

El tag es el valor numérico a la derecha del «=» y es fundamental ya que va a caracterizar al elemento en binario.

string,bool, int32, double…son algunos tipos posibles de los que dispone protocol buffer.

Comentar que a la izquierda del tipo puede añadirse un modificador (requiered,optional o repeted) que determina su obligatoriedad o no y lo que es más interesante si se trata de un array(repeated), existiendo la opción de omitirse como en nuestro caso.

service BlockChainService determina un servicio gRPC y contiene todos sus métodos RPC, en nuestro caso definimos el método addBlock siendo la  entrada y la salida Block y BlockConfirm respectivamente.

Una vez que tenemos definido el «proto» si hacemos un maven clean install de nuestro proyecto.

Vemos como se ha generado toda la estructura java necesaria del kernel de gRPC que nos va a permitir poder realizar la comunicación entre el cliente y el servidor.

Se han creado dos estructuras(*) aunque con la misma paquetización java.

……./protobuf/grpc-java/ contiene BlockChainServiceGrpc.java que corresponde con la definición en el proto:

service BlockChainService {
rpc addBlock (Block) returns (BlockConfirm);
}

……./protobuf/java/  contiene las clases java generadas que necesita a partir de la parte de mensajes del proto Block.java,BlockConfirm.java...

message Block {
string data = 1;
string hash = 2;
string link_hash = 3;
}

message BlockConfirm {
string message = 1;
}

(*) Al construir el proyecto las clases se generan en Maven siendo necesario incluirlos como source en el java build path

Una vez que tenemos toda la infraestructura grpc estamos en disposición de crear la parte del servidor. La clase debe extender de BlockChainServiceImplBase que es donde se encunetra el método java addBlock generado a partir de su definición en el proto ( rpc addBlock (Block) returns (BlockConfirm) ) por otro lado debe estar anotada mediante @GRpcService al ser un servicio rpc.

El método addBlock tiene como parámetros la request vinculada al message Block y la response que es un StreamObserver del tipo BlockConfirm vinculado a su message homónimo del proto.

En nuestro ejemplo recibimos un bloque de una cadena de bloques, validamos su autenticidad comprobando que su «linkHash» coincida con el «hash» del último bloque de la cadena, la cual tenemos en una collection Mongo. Si el bloque es correcto lo añadimos a la cadena persistiendolo y retornamos su «hash».Si el bloque no es correcto retornaremos el valor «0».

Por consiguiente , construimos la response

     BlockConfirm hash=BlockConfirm.newBuilder()
     .setMessage(returnHash).build()

La vinculamos al observador para su retorno

     responseObserver.onNext(hash);

Y consolidamos la comunicación RPC
     responseObserver.onCompleted();

Una vez que tenemos la parte del servidor vamos a crear el cliente, para ello necesitamos un método init (anotado como @PostConstruct) donde definimos el canal indicando el servidor y puerto mediante ManagedChannelBuilder  y a partir de ahí el stub (blockChainServiceBlokingStub) mediante newBlockingStub. Ésto es fundamental para establecer la comunicación RPC entre cliente y servidor.

Una vez establecido el stub construimos el método addBlock que va a envolver la llamada RPC al addBlock de la parte del servidor donde le vamos a pasar la request que como indicamos previamente se trata del nuevo bloque de la cadena de bloques que queremos incorporar a la misma

Pues bien, una vez que tenemos la infraestructura gRPC, la parte servidora y el cliente vamos a ejecutar nuestro sistema mediante Junit realizando una llamada a nuestro cliente gRPC BlockClient

Observamos en la consola la secuencia de logs…como se levanta el servidor gRPC escuchando en el puerto 6565, el establecimiento del servicio, el contenido de la información del bloque enviado desde el cliente mediante el stub, la recepción,validación y persistencia del nuevo bloque, el retorno del «hash» del nuevo bloque y la finalización de la comunicación RPC.