Capítulo 1. Introducción a gRPC
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Las aplicaciones informáticas modernas rara vez funcionan de forma aislada. Más bien, están conectadas entre sí a través de redes informáticas y se comunican y coordinan sus acciones pasándose mensajes unas a otras. Por lo tanto, un sistema de software moderno es una colección de aplicaciones de software distribuidas que se ejecutan en distintas ubicaciones de red y se comunican entre sí mediante el paso de mensajes utilizando distintos protocolos de comunicación. Por ejemplo, un sistema de software de venta al por menor en línea comprende varias aplicaciones distribuidas, como una aplicación de gestión de pedidos, una aplicación de catálogo, bases de datos, etc. Para implementar las funcionalidades empresariales de un sistema de venta al por menor online, es necesario que haya interconectividad entre esas aplicaciones distribuidas.
Nota
Arquitectura de microservicios
La arquitectura de microservicios consiste en construir una aplicación de software como una colección de servicios independientes, autónomos (desarrollados, implementados y escalados de forma independiente), orientados a la capacidad empresarial y débilmente acoplados.1
Con la llegada de la arquitectura de microservicios y la arquitectura nativa de la nube, las aplicaciones de software convencionales que se construyen para múltiples capacidades empresariales se segregan aún más en una colección de entidades de grano fino, autónomas y orientadas a la capacidad empresarial, conocidas como microservicios. Por lo tanto, un sistema de software basado en microservicios también requiere que los microservicios estén conectados a través de la red mediante técnicas de comunicación entre procesos (o entre servicios o entre aplicaciones) . Como ejemplo, si consideramos el mismo sistema de venta al por menor online implementado mediante arquitectura de microservicios, encontrarás múltiples microservicios interconectados, como gestión de pedidos, búsqueda, pago, envío, etc. A diferencia de las aplicaciones convencionales, el número de enlaces de comunicación de red prolifera debido a la naturaleza de grano fino de los microservicios. Por tanto, independientemente del estilo arquitectónico (arquitectura convencional o de microservicios) que utilices, las técnicas de comunicación entre procesos son uno de los aspectos más importantes de las modernas aplicaciones de software distribuido.
Las comunicaciones entre procesos suelen implementarse utilizando el paso de mensajes con un estilo síncrono de petición-respuesta o con estilos asíncronos basados en eventos. En el estilo de comunicación síncrono, el proceso cliente envía un mensaje de solicitud al proceso servidor a través de la red y espera un mensaje de respuesta. En la mensajería asíncrona basada en eventos, los procesos se comunican con paso asíncrono de mensajes utilizando un intermediario conocido como corredor de eventos. Dependiendo de tu caso de uso empresarial, puedes seleccionar el patrón de comunicación que quieras implementar.
Cuando se trata de construir una comunicación síncrona de tipo solicitud-respuesta para aplicaciones nativas de la nube y microservicios modernos, el enfoque más común y convencional es construirlos como servicios RESTful, en los que modelas tu aplicación o servicio como una colección de recursos a los que se puede acceder y cuyo estado se puede cambiar mediante llamadas de red que tienen lugar a través del protocolo HTTP. Sin embargo, para la mayoría de los casos de uso, los servicios RESTful son bastante voluminosos, ineficaces y propensos a errores para construir la comunicación entre procesos. A menudo se requiere una tecnología de comunicación entre procesos altamente escalable y débilmente acoplada que sea más eficiente que los servicios RESTful. Aquí es donde entra en escena gRPC, un moderno estilo de comunicación entre procesos para construir aplicaciones distribuidas y microservicios (compararemos y contrastaremos gRPC con la comunicación RESTful más adelante en este capítulo). gRPC utiliza principalmente un estilo síncrono de solicitud-respuesta para la comunicación, pero puede funcionar en modo totalmente asíncrono o de streaming una vez establecida la comunicación inicial.
En este capítulo, exploraremos qué es gRPC y las principales motivaciones para inventar un protocolo de comunicación entre procesos de este tipo. Nos sumergiremos en los bloques de construcción clave del protocolo gRPC con la ayuda de algunos casos de uso del mundo real. Además, es importante tener un conocimiento sólido de las técnicas de comunicación entre procesos y de cómo han evolucionado con el tiempo, para que puedas entender los problemas clave que gRPC intenta resolver. Así pues, vamos a recorrer esas técnicas y a comparar y contrastar cada una de ellas. Comencemos nuestra discusión sobre gRPC analizando qué es gRPC.
¿Qué es gRPC?
gRPC (la "g" significa algo diferente en cada versión de gRPC) es una tecnología de comunicación entre procesos que te permite conectar, invocar, operar y depurar aplicaciones heterogéneas distribuidas tan fácilmente como hacer una llamada a una función local.
Cuando desarrollas una aplicación gRPC, lo primero que haces es definir una interfaz de servicio. La definición de la interfaz de servicio contiene información sobre cómo puede ser consumido tu servicio por los consumidores, qué métodos permites que los consumidores llamen remotamente, qué parámetros de método y formatos de mensaje utilizar al invocar esos métodos, etc. El lenguaje que especificamos en la definición del servicio se conoce como lenguaje de definición de interfaz (IDL).
Utilizando esa definición de servicio, puedes generar el código del lado del servidor conocido como esqueleto del servidor, que simplifica la lógica del lado del servidor proporcionando abstracciones de comunicación de bajo nivel. También puedes generar el código del lado del cliente, conocido como stub del cliente, que simplifica la comunicación del lado del cliente con abstracciones para ocultar la comunicación de bajo nivel para distintos lenguajes de programación. Los métodos que especifiques en la definición de la interfaz del servicio pueden ser invocados remotamente por el lado del cliente con la misma facilidad con que se hace una invocación a una función local. El marco subyacente gRPC maneja todas las complejidades que normalmente se asocian a la aplicación de contratos de servicio estrictos, serialización de datos, comunicación de red, autenticación, control de acceso, observabilidad, etc.
Para entender los conceptos fundamentales de gRPC, echemos un vistazo a un caso de uso real de un microservicio implementado con gRPC. Supongamos que estamos construyendo una aplicación de venta online compuesta por varios microservicios. Como se ilustra en la Figura 1-1, supongamos que queremos construir un microservicio que proporcione los detalles de los productos que están disponibles en nuestra aplicación de venta online (implementaremos este caso de uso desde cero en el Capítulo 2). El servicio ProductInfo
está modelado de tal manera que se expone a través de la red como un servicio gRPC.
La definición del servicio se especifica en el archivo ProductInfo.proto, que utilizan tanto el servidor como el cliente para generar el código. En este ejemplo, hemos supuesto que el servicio se implementa utilizando el lenguaje Go y que el consumidor se implementa utilizando Java. La comunicación de red entre el servicio y el consumidor tiene lugar a través de HTTP/2.
Ahora vamos a profundizar en los detalles de esta comunicación gRPC. El primer paso para construir un servicio gRPC es crear la definición de la interfaz del servicio con los métodos que expone ese servicio, junto con los parámetros de entrada y los tipos de retorno. Pasemos a los detalles de la definición del servicio.
Definición del servicio
gRPC utiliza búferes de protocolo como IDL para definir la interfaz del servicio. Los búferes de protocolo son un mecanismo extensible, independiente del lenguaje y de la plataforma, para serializar datos estructurados (trataremos en detalle algunos de los fundamentos de los búferes de protocolo en el Capítulo 4, pero por ahora puedes pensar en ellos como un mecanismo de serialización de datos). La definición de la interfaz de servicio se especifica en un archivo proto, un archivo de texto normal con extensión .proto. Los servicios gRPC se definen en formato de búfer de protocolo ordinario, con los parámetros de los métodos RPC y los tipos de retorno especificados como mensajes de búfer de protocolo. Como la definición del servicio es una extensión de la especificación de la memoria intermedia del protocolo, se utiliza un plug-in gRPC especial para generar código a partir de tu archivo proto.
En nuestro caso de uso de ejemplo, la interfaz del servicio ProductInfo
puede definirse utilizando búferes de protocolo, como se muestra en el Ejemplo 1-1. La definición del servicio ProductInfo
se compone de una definición de la interfaz del servicio en la que especificamos los métodos remotos, sus parámetros de entrada y salida, y la definición del tipo (o formatos de mensaje) de esos parámetros.
Ejemplo 1-1. Definición del servicio gRPC del servicio ProductInfo utilizando buffers de protocolo
// ProductInfo.proto
syntax
=
"proto3"
;
package
ecommerce
;
service
ProductInfo
{
rpc
addProduct
(
Product
)
returns
(
ProductID
)
;
rpc
getProduct
(
ProductID
)
returns
(
Product
)
;
}
message
Product
{
string
id
=
1
;
string
name
=
2
;
string
description
=
3
;
}
message
ProductID
{
string
value
=
1
;
}
La definición del servicio comienza especificando la versión del búfer de protocolo (proto3) que utilizamos.
Los nombres de los paquetes se utilizan para evitar conflictos de nombres entre los tipos de mensajes del protocolo y también se utilizarán para generar código.
Definir la interfaz de servicio de un servicio gRPC.
Método remoto para añadir un producto que devuelve el ID del producto como respuesta.
Método remoto para obtener un producto basándose en el ID del producto.
Definición del formato/tipo de mensaje de
Product
.Campo (par nombre-valor) que contiene el ID del producto con números de campo únicos que se utilizan para identificar tus campos en el formato binario del mensaje.
Tipo definido por el usuario para el número de identificación del producto.
Un servicio es, por tanto, una colección de métodos (por ejemplo, addProduct
y getProduct
) que pueden invocarse a distancia. Cada método tiene parámetros de entrada y tipos de retorno que definimos como parte del servicio o que se pueden importar a la definición del búfer del protocolo.
Los parámetros de entrada y de retorno pueden ser de un tipo definido por el usuario (por ejemplo, los tipos Product
y ProductID
) o de un tipo conocido del búfer del protocolo definido en la definición del servicio. Estos tipos se estructuran como mensajes, donde cada mensaje es un pequeño registro lógico de información que contiene una serie de pares nombre-valor llamados campos. Estos campos son pares nombre-valor con números de campo únicos (por ejemplo, string id = 1
) que se utilizan para identificar sus campos en el formato binario del mensaje.
Esta definición de servicio se utiliza para construir el lado servidor y cliente de tu aplicación gRPC. En la siguiente sección, entraremos en los detalles de la implementación del servidor gRPC.
Servidor gRPC
Una vez que tengas una definición de servicio, puedes utilizarla para generar el código del lado del servidor o del lado del cliente utilizando el protoc del compilador de búferes de protocolo. Con el complemento gRPC para búferes de protocolo, puedes generar código gRPC del lado del servidor y del lado del cliente, así como el código normal del búfer de protocolo para rellenar, serializar y recuperar tus tipos de mensajes.
En el lado del servidor, éste implementa esa definición de servicio y ejecuta un servidor gRPC para gestionar las llamadas de los clientes. Por lo tanto, en el lado del servidor, para que el servicio ProductInfo
haga su trabajo tienes que hacer lo siguiente:
-
Implementa la lógica de servicio del esqueleto de servicio generado sobrescribiendo la clase base del servicio.
-
Ejecuta un servidor gRPC para escuchar las peticiones de los clientes y devolver las respuestas del servicio.
Al implementar la lógica del servicio, lo primero que hay que hacer es generar el esqueleto del servicio a partir de la definición del servicio. Por ejemplo, en el fragmento de código del Ejemplo 1-2, puedes encontrar las funciones remotas generadas para el servicio ProductInfo
construido con Go. Dentro del cuerpo de estas funciones remotas puedes implementar la lógica de cada función.
Ejemplo 1-2. Implementación en el servidor gRPC del servicio ProductInfo con Go
import
(
...
"context"
pb
"github.com/grpc-up-and-running/samples/ch02/productinfo/go/proto"
"google.golang.org/grpc"
...
)
// ProductInfo implementation with Go
// Add product remote method
func
(
s
*
server
)
AddProduct
(
ctx
context
.
Context
,
in
*
pb
.
Product
)
(
*
pb
.
ProductID
,
error
)
{
// business logic
}
// Get product remote method
func
(
s
*
server
)
GetProduct
(
ctx
context
.
Context
,
in
*
pb
.
ProductID
)
(
*
pb
.
Product
,
error
)
{
// business logic
}
Una vez que tengas lista la implementación del servicio, tienes que ejecutar un servidor gRPC que escuche las peticiones de los clientes, envíe esas peticiones a la implementación del servicio y devuelva las respuestas del servicio al cliente. El fragmento de código del Ejemplo 1-3 muestra una implementación del servidor gRPC con Go para el caso de uso del servicio ProductInfo
. Aquí abrimos un puerto TCP, iniciamos el servidor gRPC y registramos el servicio ProductInfo
en ese servidor.
Ejemplo 1-3. Ejecutar un servidor gRPC para el servicio ProductInfo con Go
func
main
()
{
lis
,
_
:=
net
.
Listen
(
"tcp"
,
port
)
s
:=
grpc
.
NewServer
()
pb
.
RegisterProductInfoServer
(
s
,
&
server
{})
if
err
:=
s
.
Serve
(
lis
);
err
!=
nil
{
log
.
Fatalf
(
"failed to serve: %v"
,
err
)
}
}
Eso es todo lo que tienes que hacer en el lado del servidor. Pasemos a la implementación de gRPC en el lado del cliente.
Cliente gRPC
De forma similar al lado del servidor, podemos generar el stub del lado del cliente utilizando la definición del servicio. El stub del cliente proporciona los mismos métodos que el servidor, que tu código cliente puede invocar; el stub del cliente los traduce a llamadas de red de invocación de funciones remotas que van al lado del servidor. Como las definiciones de servicio gRPC son independientes del idioma, puedes generar clientes y servidores para cualquier idioma compatible (a través de las implementaciones de terceros) que elijas. Así, para el caso de uso del servicio ProductInfo
, podemos generar el stub del cliente para Java, mientras que nuestro lado servidor se implementa con Go. En el fragmento de código del Ejemplo 1-4, encontrarás el código para Java. A pesar del lenguaje de programación que utilicemos, los sencillos pasos de una implementación del lado del cliente consisten en establecer una conexión con el servidor remoto, adjuntar el stub de cliente a esa conexión e invocar el método remoto utilizando el stub de cliente.
Ejemplo 1-4. Cliente gRPC para invocar un método de servicio remoto
// Create a channel using remote server address
ManagedChannel
channel
=
ManagedChannelBuilder
.
forAddress
(
"localhost"
,
8080
)
.
usePlaintext
(
true
)
.
build
();
// Initialize blocking stub using the channel
ProductInfoGrpc
.
ProductInfoBlockingStub
stub
=
ProductInfoGrpc
.
newBlockingStub
(
channel
);
// Call remote method using the blocking stub
StringValue
productID
=
stub
.
addProduct
(
Product
.
newBuilder
()
.
setName
(
"Apple iPhone 11"
)
.
setDescription
(
"Meet Apple iPhone 11."
+
"All-new dual-camera system with "
+
"Ultra Wide and Night mode."
)
.
build
());
Como ahora tienes una buena noción de los conceptos clave de gRPC, vamos a intentar comprender en detalle el flujo de mensajes cliente-servidor de gRPC.
Flujo de mensajes cliente-servidor
Cuando un cliente gRPC invoca un servicio gRPC, la biblioteca gRPC del lado del cliente utiliza el búfer de protocolo y marshala el formato del búfer de protocolo de llamada a procedimiento remoto, que luego se envía a través de HTTP/2. En el lado del servidor, la solicitud se desmarca y se ejecuta la respectiva invocación al procedimiento utilizando los búferes de protocolo. La respuesta sigue un flujo de ejecución similar desde el servidor al cliente. Como protocolo de transporte alámbrico, gRPC utiliza HTTP/2, que es un protocolo de mensajes binarios de alto rendimiento compatible con la mensajería bidireccional. En el Capítulo 4 trataremos más a fondo los detalles de bajo nivel del flujo de mensajes entre clientes y servidores gRPC, junto con los búferes de protocolo y la forma en que gRPC utiliza HTTP/2.
Nota
Marshaling es el proceso de empaquetar parámetros y una función remota en un paquete de mensajes que se envía por la red, mientras que unmarshaling desempaqueta el paquete de mensajes en la respectiva invocación al método.
Antes de profundizar en el protocolo gRPC, es importante tener un amplio conocimiento de las distintas tecnologías de comunicación entre procesos y de cómo han evolucionado con el tiempo.
Evolución de la comunicación entre procesos
Las técnicas de comunicación entre procesos han evolucionado drásticamente con el tiempo. Están surgiendo varias técnicas de este tipo para responder a las necesidades modernas y proporcionar una experiencia de desarrollo mejor y más eficaz. Por eso, es importante comprender bien cómo han evolucionado las técnicas de comunicación entre procesos y cómo han llegado a gRPC. Veamos algunas de las técnicas de comunicación entre procesos más utilizadas e intentemos compararlas y contrastarlas con gRPC.
EPR convencional
RPC era una técnica popular de comunicación entre procesos para crear aplicaciones cliente-servicio. Con la RPC, un cliente puede invocar remotamente una función de un método igual que si llamara a un método local. Hubo implementaciones populares de RPC en los primeros tiempos, como la Arquitectura de Corredores de Peticiones de Objetos Comunes (CORBA) y la Invocación Remota de Métodos (RMI) de Java, que se utilizaban para construir y conectar servicios o aplicaciones. Sin embargo, la mayoría de estas implementaciones RPC convencionales son abrumadoramente complejas, ya que se construyen sobre protocolos de comunicación como TCP, lo que dificulta la interoperabilidad, y se basan en especificaciones hinchadas.
SOAP
Debido a las limitaciones de las implementaciones RPC convencionales, como CORBA, las grandes empresas, como Microsoft, IBM, etc., diseñaron y promovieron intensamente el Protocolo Simple de Acceso a Objetos (SOAP). SOAP es la técnica de comunicación estándar en una arquitectura orientada a servicios (SOA) para intercambiar datos estructurados basados en XML entre servicios (normalmente llamados servicios web en el contexto de SOA) y se comunica a través de cualquier protocolo de comunicación subyacente, como HTTP (el más utilizado).
Con SOAP puedes definir la interfaz de servicio, las operaciones de ese servicio y un formato de mensaje XML asociado que se utilizará para invocar esas operaciones. SOAP fue una tecnología bastante popular, pero la complejidad del formato de los mensajes, así como las complejidades de las especificaciones construidas en torno a SOAP, dificultan la agilidad de la construcción de aplicaciones distribuidas. Por lo tanto, en el contexto del desarrollo de aplicaciones distribuidas modernas, los servicios web SOAP se consideran una tecnología heredada. En lugar de utilizar SOAP, la mayoría de las aplicaciones distribuidas existentes se desarrollan ahora utilizando el estilo de arquitectura REST.
REST
La Transferencia de Estado Representacional (REST) es un estilo arquitectónico que tiene su origen en la tesis doctoral de Roy Fielding. Fielding es uno de los principales autores de la especificación HTTP y el creador del estilo arquitectónico REST. REST es la base de la arquitectura orientada a recursos (ROA), en la que modelas las aplicaciones distribuidas como una colección de recursos y los clientes que acceden a esos recursos pueden cambiar el estado (crear, leer, actualizar o eliminar) de esos recursos.
La implementación de facto de REST es HTTP, y en HTTP puedes modelar una aplicación web RESTful como una colección de recursos accesibles mediante un identificador único (URL). Las operaciones de cambio de estado se aplican sobre esos recursos en forma de verbos HTTP (GET, POST, PUT, DELETE, PATCH, etc.). El estado de los recursos se representa en formatos textuales como JSON, XML, HTML, YAML, etc.
Construir aplicaciones utilizando el estilo arquitectónico REST con HTTP y JSON se ha convertido en el método de facto para construir microservicios. Sin embargo, con la proliferación del número de microservicios y sus interacciones en red, los servicios RESTful no han podido satisfacer los requisitos modernos esperados. Hay un par de limitaciones clave de los servicios RESTful que dificultan su uso como protocolo de mensajería para las aplicaciones modernas basadas en microservicios.
Protocolos ineficaces de mensajes basados en texto
Intrínsecamente, los servicios RESTful se construyen sobre protocolos de transporte basados en texto, como HTTP 1.x, y aprovechan formatos textuales legibles por humanos, como JSON. Cuando se trata de la comunicación entre servicios, es bastante ineficaz utilizar un formato textual como JSON, porque ambas partes de esa comunicación no necesitan utilizar esos formatos textuales legibles por humanos.
La aplicación cliente (fuente) produce contenido binario para enviarlo al servidor, luego convierte la estructura binaria en texto (porque con HTTP 1.x hay que enviar mensajes textuales) y lo envía por la red en texto (por HTTP) a una máquina que lo analiza y lo convierte de nuevo en una estructura binaria en el lado del servicio (destino). En lugar de eso, podríamos haber enviado fácilmente un formato binario que se pueda asignar a la lógica empresarial de un servicio y un consumidor. Un argumento popular para utilizar JSON es que es más fácil de usar porque es "legible por humanos". Esto es más un problema de herramientas que un problema con los protocolos binarios.
Carece de interfaces fuertemente tipadas entre aplicaciones
Con el creciente número de servicios que interactúan a través de la red y que se construyen con tecnologías políglotas dispares, la falta de definiciones de servicio bien definidas y fuertemente tipadas supuso un gran contratiempo. La mayoría de las tecnologías de definición de servicios existentes en los servicios RESTful, como OpenAPI/Swagger, son de última hora y no están estrechamente integradas con el estilo arquitectónico o los protocolos de mensajería subyacentes .
Esto da lugar a muchas incompatibilidades, errores en tiempo de ejecución y problemas de interoperabilidad al crear estas aplicaciones descentralizadas. Por ejemplo, cuando desarrollas servicios RESTful, no es necesario tener una definición de servicio y de tipo de la información que se comparte entre las aplicaciones. Más bien, desarrollas tus aplicaciones RESTful fijándote en el formato textual en el cable o en tecnologías de definición de API de terceros, como OpenAPI. Por tanto, disponer de una tecnología moderna de definición de servicios fuertemente tipada y de un marco que genere el núcleo del código del lado del servidor y del lado del cliente para las tecnologías políglotas es una necesidad clave.
El estilo arquitectónico REST es difícil de aplicar
Como estilo arquitectónico, REST tiene muchas "buenas prácticas" que debes seguir para crear un verdadero servicio RESTful. Pero no se aplican como parte de los protocolos de implementación (como HTTP), lo que dificulta su cumplimiento en la fase de implementación. Por lo tanto, en la práctica, la mayoría de los servicios que dicen ser RESTful no siguen correctamente los fundamentos del estilo REST. Así, la mayoría de los llamados servicios RESTful no son más que servicios HTTP expuestos a través de la red. Por tanto, los equipos de desarrollo tienen que dedicar mucho tiempo a mantener la coherencia y pureza de un servicio RESTful.
Con todas estas limitaciones de las técnicas de comunicación entre procesos en la construcción de aplicaciones modernas nativas de la nube, comenzó la búsqueda para inventar un protocolo de mensajes mejor.
Inicio del gRPC
Google había estado utilizando un marco RPC de propósito general llamado Stubby para conectar miles de microservicios que se ejecutan en múltiples centros de datos y construidos con tecnologías dispares. Su capa central RPC se diseñó para gestionar una escala de Internet de decenas de miles de millones de solicitudes por segundo. Stubby tiene muchas funciones estupendas, pero no está estandarizado para ser utilizado como marco genérico, ya que está demasiado estrechamente acoplado a la infraestructura interna de Google.
En 2015, Google lanzó gRPC como un marco RPC de código abierto; es una infraestructura RPC estandarizada, de uso general y multiplataforma. gRPC pretendía proporcionar la misma escalabilidad, rendimiento y funcionalidad que ofrecía Stubby, pero a la comunidad en general.
Desde entonces, la popularidad de gRPC ha crecido espectacularmente en los últimos años con la adopción a gran escala por parte de grandes empresas como Netflix, Square, Lyft, Docker, Cisco y CoreOS. Más tarde, gRPC se unió a la Cloud Native Computing Foundation (CNCF), una de las fundaciones de software de código abierto más populares dedicada a hacer que la computación nativa en la nube sea universal y sostenible; gRPC ganó mucha tracción gracias a los proyectos del ecosistema CNCF.
Veamos ahora algunas de las razones clave para utilizar gRPC en lugar de los protocolos convencionales de comunicación entre procesos.
¿Por qué gRPC?
gRPC está diseñado para ser una tecnología de comunicación entre procesos a escala de Internet, capaz de superar la mayoría de las deficiencias de las tecnologías convencionales de comunicación entre procesos. Debido a las ventajas de gRPC, la mayoría de las aplicaciones y servidores modernos están convirtiendo cada vez más su protocolo de comunicación entre procesos a gRPC. Entonces, ¿por qué alguien elegiría gRPC como protocolo de comunicación cuando hay tantas otras opciones disponibles? Veamos más detenidamente algunas de las principales ventajas que aporta gRPC.
Ventajas de gRPC
Las ventajas que aporta gRPC son clave para su creciente adopción. Estas ventajas son las siguientes
- Es eficaz para la comunicación entre procesos
-
En lugar de utilizar un formato textual como JSON o XML, gRPC utiliza un protocolo binario basado en búferes de protocolo para comunicarse con los servicios y clientes gRPC. Además, gRPC implementa búferes de protocolo sobre HTTP/2, lo que lo hace aún más rápido para la comunicación entre procesos. Esto convierte a gRPC en una de las tecnologías de comunicación entre procesos más eficientes que existen.
- Tiene interfaces de servicio y esquemas sencillos y bien definidos
-
gRPC fomenta un enfoque de "primero el contrato" para desarrollar aplicaciones. Primero defines las interfaces de servicio y después trabajas en los detalles de implementación. Así, a diferencia de OpenAPI/Swagger para la definición de servicios RESTful y WSDL para los servicios web SOAP, gRPC ofrece una experiencia de desarrollo de aplicaciones sencilla pero coherente, fiable y escalable.
- Está fuertemente tipado
-
Dado que utilizamos buffers de protocolo para definir los servicios gRPC, los contratos de servicio gRPC definen claramente los tipos que utilizarás para la comunicación entre las aplicaciones. Esto hace que el desarrollo de aplicaciones distribuidas sea mucho más estable, ya que la tipificación estática ayuda a superar la mayoría de los errores de tiempo de ejecución e interoperabilidad que encontrarías al crear aplicaciones nativas de la nube que abarcan varios equipos y tecnologías.
- Es políglota
-
gRPC está diseñado para funcionar con múltiples lenguajes de programación. La definición de un servicio gRPC con búferes de protocolo es independiente del lenguaje. Por tanto, puedes elegir el lenguaje que prefieras, pero puedes interoperar con cualquier servicio o cliente gRPC existente.
- Tiene transmisión dúplex
-
gRPC tiene soporte nativo para streaming del lado del cliente o del servidor, que está integrado en la propia definición del servicio. Esto facilita mucho el desarrollo de servicios o clientes de streaming. Y la capacidad de crear mensajería convencional de tipo solicitud-respuesta y streaming del lado del cliente y del servidor es una ventaja clave sobre el estilo de mensajería RESTful convencional.
- Lleva incorporadas funciones de materias primas
-
gRPC ofrece soporte integrado para funciones básicas como autenticación, encriptación, resistencia (plazos y tiempos de espera), intercambio de metadatos, compresión, equilibrio de carga, descubrimiento de servicios, etc. (las exploraremos en el Capítulo 5).
- Está integrado con los ecosistemas nativos de la nube
-
gRPC forma parte del CNCF y la mayoría de los marcos de trabajo y tecnologías modernas ofrecen soporte nativo para gRPC de forma inmediata. Por ejemplo, muchos proyectos del CNCF, como Envoy, admiten gRPC como protocolo de comunicación; para funciones transversales como las métricas y el monitoreo, la mayoría de estas herramientas admiten gRPC (por ejemplo, utilizando Prometheus para monitorear aplicaciones gRPC).
- Está maduro y ha sido ampliamente adoptado
-
gRPC ha madurado gracias a sus intensas pruebas de batalla en Google, y muchas otras grandes empresas tecnológicas como Square, Lyft, Netflix, Docker, Cisco y CoreOS lo han adoptado.
Como cualquier tecnología, gRPC también tiene una serie de inconvenientes. Conocer esos inconvenientes durante el desarrollo de una aplicación es bastante útil. Así que echemos un vistazo a algunas de las limitaciones de gRPC.
Desventajas del gRPC
He aquí algunos de los inconvenientes de gRPC que debes tener en cuenta cuando lo elijas para aplicaciones de construcción. Entre ellas están las siguientes
- Puede no ser adecuado para servicios de cara al exterior
-
Cuando quieras exponer la aplicación o los servicios a un cliente externo a través de Internet, es posible que gRPC no sea el protocolo más adecuado, ya que la mayoría de los consumidores externos conocen bastante bien gRPC y REST/HTTP. La naturaleza fuertemente tipada y basada en contratos de los servicios gRPC puede dificultar la flexibilidad de los servicios que expones a las partes externas, y los consumidores tienen mucho menos control (a diferencia de protocolos como GraphQL, que se explica en la siguiente sección). La pasarela gRPC está diseñada como una solución para superar este problema. Hablaremos de ella en detalle en el Capítulo 8.
- Los cambios drásticos en la definición del servicio son un proceso de desarrollo complicado
-
Las modificaciones de esquema son bastante comunes en los casos de uso modernos de comunicación entre servicios. Cuando se producen cambios drásticos en la definición del servicio gRPC, normalmente hay que regenerar el código tanto del cliente como del servidor. Esto debe incorporarse al proceso de integración continua existente y puede complicar el ciclo de vida general del desarrollo. Sin embargo, la mayoría de los cambios en la definición del servicio gRPC pueden acomodarse sin romper el contrato de servicio, y gRPC interoperará felizmente con clientes y servidores que utilicen versiones diferentes de un proto, siempre que no se introduzcan cambios de ruptura. Así que la regeneración de código no es necesaria en la mayoría de los casos.
- El ecosistema es relativamente pequeño
-
El ecosistema gRPC es aún relativamente pequeño en comparación con el protocolo REST/HTTP convencional. El soporte para gRPC en aplicaciones de navegador y móviles está aún en fases primitivas.
Debes tener en cuenta estas limitaciones a la hora de desarrollar aplicaciones. Así que, obviamente, gRPC no es una técnica que debas utilizar para todos tus requisitos de comunicación entre procesos. Más bien, debes evaluar el caso de uso y los requisitos de la empresa y elegir el protocolo de mensajería adecuado. Exploraremos algunas de estas directrices en el Capítulo 8.
Como hemos comentado en secciones anteriores, existen muchas técnicas de comunicación entre procesos, tanto existentes como emergentes. Es importante comprender bien cómo podemos comparar gRPC con otras tecnologías similares que han ganado popularidad en el panorama moderno del desarrollo de aplicaciones, ya que esto te ayudará a encontrar el protocolo más adecuado para tus servicios.
gRPC frente a otros protocolos: GraphQL y Thrift
Hemos tratado en detalle algunas de las principales limitaciones de REST, que sentaron las bases para el inicio de gRPC. Del mismo modo, están surgiendo bastantes tecnologías de comunicación entre procesos para satisfacer las mismas necesidades. Así pues, veamos algunas de las tecnologías más populares y comparémoslas con gRPC.
Ahorro Apache
Apache Thrift es un marco RPC (desarrollado inicialmente en Facebook y donado posteriormente a Apache) similar a gRPC. Utiliza su propio lenguaje de definición de interfaces y ofrece soporte para una amplia gama de lenguajes de programación. Thrift te permite definir tipos de datos e interfaces de servicio en un archivo de definición. Tomando la definición del servicio como entrada, el compilador de Thrift genera código para los lados cliente y servidor. La capa de transporte de Thrift proporciona abstracciones para la E/S de red y desacopla Thrift del resto del sistema, lo que significa que puede ejecutarse en cualquier implementación de transporte como TCP, HTTP, etc.
Si comparas Thrift con gRPC, verás que ambos siguen más o menos los mismos objetivos de diseño y uso. Sin embargo, hay varios diferenciadores importantes entre ambos:
- Transporte
-
gRPC es más opinable que Thrift y ofrece soporte de primera clase para HTTP/2. Sus implementaciones en HTTP/2 aprovechan las capacidades del protocolo para lograr eficiencia y compatibilidad con patrones de mensajería como el streaming.
- Streaming
-
Las definiciones de servicio gRPC soportan de forma nativa la transmisión bidireccional (cliente y servidor) como parte de la propia definición del servicio.
- Adopción y comunidad
-
En lo que respecta a la adopción, gRPC parece tener un impulso bastante bueno y ha conseguido construir un buen ecosistema en torno a los proyectos del CNCF. Además, los recursos de la comunidad, como una buena documentación, presentaciones externas y ejemplos de casos de uso, son bastante comunes para gRPC, lo que hace que el proceso de adopción sea más fluido en comparación con Thrift.
- Rendimiento
-
Aunque no hay resultados oficiales que comparen gRPC con Thrift, hay algunos recursos en línea con comparaciones de rendimiento entre ambos que muestran mejores cifras para Thrift. Sin embargo, el rendimiento de gRPC también se está comparando en gran medida en casi todas las versiones. Así que es poco probable que el rendimiento sea un factor decisivo a la hora de elegir Thrift en lugar de gRPC. Además, hay otros marcos RPC que ofrecen capacidades similares, pero gRPC está actualmente a la cabeza como la tecnología RPC más estandarizada, interoperable y ampliamente adoptada.
GraphQL
GraphQL es otra tecnología (inventada por Facebook y estandarizada como tecnología abierta) que se está haciendo bastante popular para construir la comunicación entre procesos. Es un lenguaje de consulta para API y un tiempo de ejecución para realizar esas consultas con tus datos existentes. GraphQL ofrece un enfoque fundamentalmente diferente para la comunicación convencional cliente-servidor, al permitir que los clientes determinen qué datos quieren, cómo los quieren y en qué formato los quieren. gRPC, en cambio, tiene un contrato fijo para los métodos remotos que permiten la comunicación entre el cliente y el servidor.
GraphQL es más adecuado para servicios externos o API que se exponen directamente a los consumidores, donde los clientes necesitan más control sobre los datos que consumen del servidor. Por ejemplo, en nuestro escenario de aplicación minorista en línea, supongamos que los consumidores del servicio ProductInfo
sólo necesitan información específica sobre los productos, pero no todo el conjunto de atributos de un producto, y los consumidores también necesitan una forma de especificar la información que desean. Con GraphQL puedes modelar un servicio de modo que permita a los consumidores consultar el servicio utilizando el lenguaje de consulta GraphQL y obtener la información requerida.
En la mayoría de los casos de uso pragmático de GraphQL y gRPC, GraphQL se utiliza para los servicios/API de cara al exterior, mientras que los servicios internos que respaldan las API se implementan utilizando gRPC.
Ahora echemos un vistazo a algunos de los adoptantes de gRPC en el mundo real y a sus casos de uso.
gRPC en el mundo real
El éxito de cualquier protocolo de comunicación entre procesos depende en gran medida de su adopción en todo el sector y de la comunidad de usuarios y desarrolladores que respalde ese proyecto. gRPC ha sido ampliamente adoptado para construir microservicios y aplicaciones nativas de la nube. Veamos algunas de las principales historias de éxito de gRPC.
Netflix
Netflix, una empresa de streaming de vídeo por suscripción, es una de las pioneras en practicar la arquitectura de microservicios a escala. Todas sus capacidades de streaming de vídeo se ofrecen a los consumidores a través de un servicio gestionado externo (o API) y hay cientos de servicios backend que respaldan sus API. Por tanto, la comunicación entre procesos (o entre servicios) es uno de los aspectos más importantes de su caso de uso. Durante la fase inicial de implementación de microservicios, Netflix desarrolló su propia pila tecnológica para la comunicación interservicios utilizando servicios RESTful en HTTP/1.1, que respalda casi el 98% de los casos de uso empresarial del producto Netflix.
Sin embargo, Netflix ha observado varias limitaciones del enfoque basado en servicios RESTful cuando operan a escala de Internet. Los consumidores de microservicios RESTful a menudo se escribían desde cero, inspeccionando los recursos y los formatos de mensaje necesarios de los servicios RESTful. Esto consumía mucho tiempo, dificultaba la productividad de los desarrolladores y también aumentaba el riesgo de un código más propenso a errores. La implementación y el consumo de servicios también suponía un reto debido a la falta de tecnologías para una definición completa de la interfaz de un servicio. Por ello, inicialmente intentó superar la mayoría de estas limitaciones construyendo un marco RPC interno, pero tras evaluar las pilas tecnológicas disponibles, eligió gRPC como su tecnología de comunicación entre servicios. Durante su evaluación, Netflix descubrió que gRPC se situaba cómodamente a la cabeza en cuanto a encapsular todas las responsabilidades necesarias en un paquete fácil de consumir.
Con la adopción de gRPC, Netflix ha experimentado un enorme aumento de la productividad de los desarrolladores. Por ejemplo, para cada cliente, cientos de líneas de código personalizado se sustituyen por sólo dos o tres líneas de configuración en el proto. Crear un cliente, que podía llevar hasta dos o tres semanas, es cuestión de minutos con gRPC. La estabilidad general de la plataforma también ha mejorado mucho porque ya no se necesita código escrito a mano para la mayoría de las funciones básicas y existe una forma completa y segura de definir las interfaces de servicio. Gracias al aumento del rendimiento que proporciona gRPC, se ha reducido la latencia general de toda la plataforma de Netflix. Dado que ha adoptado gRPC para la mayoría de sus casos de uso de comunicación entre procesos, parece que Netflix ha puesto en modo de mantenimiento (no en desarrollo activo) algunos de sus proyectos de cosecha propia (por ejemplo, Ribbon) que están construidos para la comunicación entre procesos utilizando los protocolos REST y HTTP, y en su lugar está utilizando gRPC.
etcd
etcd es un almacén distribuido fiable de clave-valor para los datos más críticos de un sistema distribuido. Es uno de los proyectos de código abierto más populares en CNCF y muy adoptado por muchos otros proyectos de código abierto como Kubernetes. Un factor clave del éxito de gRPC es que tiene una API de cara al usuario sencilla, bien definida y fácil de consumir. etcd utiliza una API de cara al usuario de gRPC para aprovechar toda la potencia de gRPC.
Dropbox
Dropbox es un servicio de alojamiento de archivos que ofrece almacenamiento en la nube, sincronización de archivos, nube personal y software cliente. Dropbox ejecuta cientos de microservicios políglotas, que intercambian millones de peticiones por segundo. Inicialmente utilizaba varios marcos RPC, incluido un marco RPC propio con un protocolo personalizado para la serialización y deserialización manual, Apache Thrift, y un marco RPC heredado que era un protocolo basado en HTTP/1.1 con mensajes codificados con protobuf.
En lugar de utilizar cualquiera de ellos, Dropbox se ha pasado a gRPC (que también le permite reutilizar algunas de las definiciones de búfer de protocolo existentes de sus formatos de mensaje). Ha creado Courier, un marco RPC basado en gRPC. Courier no es un nuevo protocolo RPC, sino un proyecto que integra gRPC con la infraestructura existente de Dropbox. Dropbox ha aumentado gRPC para satisfacer sus requisitos específicos relacionados con la autenticación, la autorización, el descubrimiento de servicios, las estadísticas de servicios, el registro de eventos y las herramientas de rastreo.
Estas historias de éxito de gRPC nos dicen que es un protocolo de mensajería entre procesos que es sencillo, aumenta la productividad y la fiabilidad, y se escala y funciona a escala de Internet. Estos son algunos de los primeros usuarios conocidos de gRPC, pero los casos de uso y la adopción de gRPC son cada vez mayores.
Resumen
Las aplicaciones o servicios de software modernos rara vez viven aislados, y las técnicas de comunicación entre procesos que los conectan son uno de los aspectos más importantes de las aplicaciones de software distribuidas modernas. gRPC es una solución escalable, de acoplamiento suelto y de tipo seguro que permite una comunicación entre procesos más eficiente que la comunicación convencional basada en REST/HTTP. Te permite conectar, invocar, operar y depurar aplicaciones heterogéneas distribuidas tan fácilmente como hacer una llamada a un método local a través de protocolos de transporte de red como HTTP/2.
gRPC también puede considerarse una evolución de los RPC convencionales y ha conseguido superar sus limitaciones. gRPC está siendo ampliamente adoptado por varias empresas a escala de Internet para sus necesidades de comunicación entre procesos y se utiliza sobre todo para construir comunicaciones internas de servicio a servicio.
Los conocimientos que adquieras en este capítulo serán un buen punto de partida para el resto de los capítulos, en los que profundizarás en distintos aspectos de la comunicación gRPC. Estos conocimientos se pondrán en práctica en el siguiente capítulo, en el que construiremos una aplicación gRPC del mundo real desde cero.
1 K. Indrasiri y P. Siriwardena, Microservicios para la empresa (Apress, 2018).
Get gRPC: funcionando now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.