A medida que avanzamos en el desarrollo de aplicaciones, entre varias cosas, hay una principal de la que estamos menos preocupados: el poder de cómputo. Debido a la llegada de los proveedores de nube, estamos menos preocupados por administrar centros de datos. Todo está disponible en cuestión de segundos bajo demanda. Esto también lleva a un aumento en el tamaño de los datos. Se generan y transportan grandes volúmenes de datos utilizando varios medios en solicitudes únicas.
Con el aumento del tamaño de los datos, se agregan actividades como la serialización, la deserialización y los costos de transporte. Aunque no nos preocupamos por los recursos de cómputo, la latencia se convierte en un gasto general. Necesitamos reducir el transporte. Se han desarrollado muchos protocolos de mensajería en el pasado para abordar esto. SOAP era voluminoso, y REST es una versión reducida, pero necesitamos un marco aún más eficiente. Ahí es donde entran las Llamadas a Procedimientos Remotos —RPC—.
En este artículo, vamos a entender qué es RPC, y las diversas implementaciones de RPC, con un enfoque en gRPC, que es la implementación de RPC de Google. También compararemos REST con RPC y entenderemos varios aspectos de gRPC, incluida la seguridad, las herramientas y mucho más.
¿Qué es RPC?
RPC significa Llamadas a Procedimientos Remotos. La definición está en el nombre mismo. Las llamadas a procedimientos simplemente significan llamadas a funciones/métodos; es la palabra “Remoto” lo que marca toda la diferencia. ¿Y si podemos hacer una llamada a una función de manera remota?
Simplemente, si una función reside en un servidor y para ser invocada desde el lado del cliente, ¿podríamos hacerlo tan simple como una llamada a método/función? Esencialmente, lo que hace un RPC es dar la ilusión al cliente de que está invocando un método local, pero en realidad, invoca un método en una máquina remota que abstrae las tareas de la capa de red. Lo hermoso de esto es que el contrato se mantiene muy estricto y transparente (discutiremos esto más adelante en el artículo).
Pasos involucrados en una llamada RPC:
Esto es lo que parece un proceso típico de REST:
Los RPC simplifican el proceso a lo siguiente:
Esto se debe a que todas las complicaciones asociadas con hacer una solicitud ahora están abstraídas de nosotros (discutiremos esto en generación de código). Todo lo que debemos preocuparnos es por los datos y la lógica.
gRPC: Qué, Por Qué y Cómo de Esto
Hasta ahora hemos discutido RPC, que esencialmente significa hacer llamadas a funciones/métodos de forma remota, lo que nos brinda beneficios como “definición de contrato estricta,” “abstracción de transmisión y conversión de datos,” “reducción de latencia,” y otros, que discutiremos a medida que avanzamos con esta publicación. Lo que realmente nos gustaría profundizar es una de las implementaciones de RPC. RPC es un concepto y gRPC es un marco basado en él.
Existen varias implementaciones de RPC. Son:
-
gRPC (google)
-
Thrift (Facebook)
-
Finalge (Twitter)
La versión de RPC de Google se conoce como gRPC. Fue introducido en 2015 y ha ido ganando popularidad desde entonces. Es uno de los mecanismos de comunicación más elegidos en una arquitectura de microservicios.
gRPC utiliza protocol buffers (un formato de mensaje de código abierto) como el método predeterminado de comunicación entre cliente y servidor. Además, gRPC emplea HTTP/2 como protocolo predeterminado. Hay cuatro tipos de comunicación que gRPC soporta:
-
Unario [comunicación típica entre cliente y servidor]
Pasando al formato de mensaje que se utiliza ampliamente en gRPC — protocol buffers, también conocidos como protobufs. Un mensaje protobuf se ve de la siguiente manera:
message Person { |
Aquí, ‘Person’ es el mensaje que queremos transferir (como parte de la solicitud/respuesta) que tiene los campos ‘name’ (tipo string), ‘id’ (tipo string) y ’email’ (tipo string). Los números 1, 2, 3 representan la posición de los datos (como en ‘name’, ‘id’ y ‘has_ponycopter’) cuando se serializan al formato binario.
Una vez que el desarrollador ha creado el archivo(s) de Protocol Buffers con todos los mensajes, podemos utilizar un compilador de protocol buffers (un binario) para compilar el archivo de protocol buffer escrito, lo cual generará todas las clases y métodos de utilidad necesarios para trabajar con el mensaje. Por ejemplo, como se muestra aquí, el código generado (dependiendo de la lengua elegida) se verá como esto.
¿Cómo Definimos Servicios?
Necesitamos definir servicios que utilicen los mensajes mencionados para ser enviados/recibidos.
Después de escribir los tipos de mensajes de solicitud y respuesta necesarios, el siguiente paso es escribir el servicio en sí.
Los servicios gRPC también se definen en Protocol Buffers y utilizan las palabras clave “service” y “RPC” para definir un servicio.
Echa un vistazo al contenido del archivo proto a continuación:
message HelloRequest { message HelloResponse { service HelloService { |
Aquí, HelloRequest y HelloResponse son los mensajes y HelloService está exponiendo un RPC unario llamado SayHello que toma HelloRequest como entrada y devuelve HelloResponse como salida.
Como se mencionó, HelloService actualmente contiene un único RPC unario. Pero podría contener más de un RPC. Además, puede contener una variedad de RPC (unario/streaming del lado del cliente/streaming del lado del servidor/Bidireccional).
Para definir un RPC de streaming, todo lo que tienes que hacer es anteponer ‘stream ‘ antes del argumento de solicitud/respuesta, Definiciones de proto para RPCs de streaming, y código generado.
En el enlace del repositorio de código anterior:
-
streaming.proto: este archivo es definido por el usuario
-
streaming.pb.go & streaming_grpc.pb.go: estos archivos se generan automáticamente al ejecutar el comando compilador-proto.
gRPC Vs. REST
Hemos hablado bastante sobre gRPC. También se mencionó REST. Lo que nos faltó fue discutir la diferencia. Quiero decir, cuando tenemos un marco de comunicación bien establecido y ligero en forma de REST, ¿por qué hubo necesidad de buscar otro marco de comunicación? Entendamos más sobre gRPC en relación con REST, junto con los pros y los contras de cada uno.
Para comparar, lo que requerimos son parámetros. Así que desglosemos la comparación en los siguientes parámetros:
-
Formato de mensaje: protocol buffers vs JSON
-
La velocidad de serialización y deserialización es mucho mejor en el caso de los protocol buffers para todos los tamaños de datos (pequeños/medios/grandes). Resultados de Pruebas de Rendimiento.
-
Después de la serialización, JSON es legible para humanos, mientras que los protobufs (en formato binario) no lo son. No estoy seguro si esto es una desventaja o no, porque a veces te gustaría ver los detalles de la solicitud en la herramienta de desarrolladores de Google o en temas de Kafka, y en el caso de los protobufs no puedes entender nada.
-
-
Protocolo de comunicación: HTTP 1.1 vs. HTTP/2T
-
REST se basa en HTTP 1.1; la comunicación entre un cliente REST y un servidor requeriría una conexión TCP establecida que a su vez implica un intercambio de tres mensajes. Cuando recibimos una respuesta del servidor tras enviar una solicitud desde el cliente, la conexión TCP no existe después de eso. Se necesita crear una nueva conexión TCP para procesar otra solicitud. Este establecimiento de una conexión TCP en cada solicitud aumenta la latencia.
-
Entonces, gRPC, que se basa en HTTP 2, ha enfrentado este desafío manteniendo una conexión persistente. Debemos recordar que las conexiones persistentes en HTTP 2 son diferentes de las que se utilizan en las websockets, donde una conexión TCP es interceptada y la transferencia de datos no se supervisa. En una conexión gRPC, una vez que se establece una conexión TCP, se reutiliza para varias solicitudes. Todas las solicitudes de un par de cliente y servidor se multiplexan en la misma conexión TCP.
-
-
Solo preocuparse por datos y lógica: La generación de código como ciudadana de primera clase
-
Las características de generación de código son nativas en gRPC a través de su compilador protoc incorporado. Con las API REST, es necesario utilizar una herramienta de terceros como Swagger para auto-generar el código para las llamadas API en varios lenguajes.
-
En el caso de gRPC, abstrae el proceso de marshalling/unmarshalling, establecimiento de conexión y envío/recepción de mensajes; lo único que debemos preocuparnos es por los datos que queremos enviar o recibir y la lógica.
-
-
Velocidad de transmisión
-
Dado que el formato binario es mucho más ligero que el formato JSON, la velocidad de transmisión en el caso de gRPC es 7 a 10 veces más rápida que la de REST.
-
Característica |
REST |
gRPC |
Protocolo de comunicación |
Sigue el modelo de solicitud-respuesta. Puede funcionar con cualquier versión de HTTP pero generalmente se usa con HTTP 1.1 |
Sigue el modelo de cliente-respuesta y se basa en HTTP 2. Algunos servidores tienen soluciones alternativas para hacerlo funcionar con HTTP 1.1 (a través de puertas de enlace REST) |
Compatibilidad con navegadores |
Funciona en todas partes |
Soporte limitado. Es necesario utilizar gRPC-Web, que es una extensión para la web y se basa en HTTP 1.1 |
Estructura de datos de carga útil |
Utiliza principalmente cargas útiles basadas en JSON y XML para transmitir datos |
Utiliza protocol buffers de forma predeterminada para transmitir cargas útiles |
Generación de código |
Es necesario utilizar herramientas de terceros como Swagger para generar código de cliente |
gRPC cuenta con soporte nativo para la generación de código en varios lenguajes |
Almacenamiento en caché de solicitudes |
Es fácil almacenar en caché solicitudes en el lado cliente y servidor. La mayoría de clientes/servidores lo soportan de forma nativa (por ejemplo, a través de cookies) |
No soporta el almacenamiento en caché de solicitudes/respuestas de forma predeterminada |
Nuevamente, por el momento, gRPC no tiene soporte para navegadores ya que la mayoría de los frameworks de UI aún tienen un soporte limitado o nulo para gRPC. Aunque gRPC es una opción automática en la mayoría de los casos cuando se trata de comunicación entre microservicios internos, no es lo mismo para la comunicación externa que requiere integración de UI.
Ahora que hemos hecho una comparación de ambos frameworks: gRPC y REST. ¿Cuál usar y cuándo?
-
En una arquitectura de microservicios con múltiples microservicios ligeros, donde la eficiencia de la transmisión de datos es fundamental, gRPC sería una elección ideal.
-
Si la generación de código con soporte para múltiples lenguajes es un requisito, gRPC debería ser el marco de trabajo a elegir.
-
Con las capacidades de streaming de gRPC, aplicaciones en tiempo real como operaciones de trading o OTT se beneficiarían de él en lugar de utilizar REST con técnicas de polling.
-
Si el ancho de banda es una restricción, gRPC proporcionaría una latencia y un rendimiento mucho más bajos.
-
Si se requiere un desarrollo más rápido y una iteración de alta velocidad, REST debería ser la opción a elegir.
Conceptos de gRPC
Balanceo de Carga
Aunque la conexión persistente resuelve el problema de la latencia, plantea otro desafío en forma de balanceo de carga. Dado que gRPC (o HTTP2) crea conexiones persistentes, incluso con la presencia de un balanceador de carga, el cliente forma una conexión persistente con el servidor que está detrás del balanceador de carga. Esto es análogo a una sesión pegajosa.
Podemos entender el problema o desafío mediante una demostración. Y el código y los archivos de despliegue se encuentran en: https://github.com/infracloudio/grpc-blog/tree/master/grpc-loadbalancing.
A partir de la base de código de la demostración anterior, podemos descubrir que la responsabilidad del equilibrio de carga recae sobre el cliente. Esto lleva a la conclusión de que la ventaja de gRPC, es decir, la conexión persistente, no existe con este cambio. Pero gRPC aún puede ser utilizado por sus otros beneficios.
Más información sobre equilibrio de carga en gRPC.
En la base de código de la demostración anterior, solo se utiliza/demuestra una estrategia de equilibrio de carga round-robin. Pero gRPC también admite otra estrategia de equilibrio de carga basada en el cliente OOB llamada “pick-first”.
Además, también se admite el equilibrio de carga personalizado en el lado del cliente.
Contrato Limpio
En REST, el contrato entre el cliente y el servidor se documenta pero no es estricto. Si retrocedemos aún más a SOAP, los contratos se exponían a través de archivos wsdl. En REST, exponemos contratos a través de Swagger y otras disposiciones. Pero la rigurosidad falta, no podemos estar seguros de si el contrato ha cambiado en el lado del servidor mientras se desarrolla el código del cliente.
Con gRPC, el contrato, ya sea mediante archivos proto o el stub generado a partir de archivos proto, se comparte tanto con el cliente como con el servidor. Esto es como hacer una llamada a una función pero de manera remota. Y dado que estamos haciendo una llamada a una función, sabemos exactamente lo que necesitamos enviar y lo que esperamos como respuesta. La complejidad de establecer conexiones con el cliente, cuidar la seguridad, la serialización-deserialización, etc., se abstrae. Todo lo que nos importa es los datos.
Considere la siguiente base de código:
https://github.com/infracloudio/grpc-blog/tree/master/greet_app
El cliente utiliza el stub (código generado desde el archivo proto) para crear un objeto de cliente e invocar la llamada de función remota:
```sh
import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
cc, err := grpc.Dial(“<server-address>”, opts)
if err != nil {
log.Fatalf("could not connect: %v", err)
}
c := greetpb.NewGreetServiceClient(cc)
res, err := c.Greet(context.Background(), req)
if err != nil {
log.Fatalf("error while calling greet rpc : %v", err)
}
```
Del mismo modo, el servidor también utiliza el mismo stub (código generado desde el archivo proto) para recibir el objeto de solicitud y crear el objeto de respuesta:
```sh
import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
func (*server) Greet(_ context.Context, req *greetpb.GreetingRequest) (*greetpb.GreetingResponse, error) {
// hacer algo con 'req'
return &greetpb.GreetingResponse{
Result: result,
}, nil
}
```
Ambos están utilizando el mismo stub generado desde el archivo proto que reside aquí.
Y el stub fue generado utilizando el siguiente comando del compilador proto.
```sh
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative internal/pkg/proto/*.proto
```
Seguridad
La autenticación y autorización gRPC opera a dos niveles:
-
La autenticación/autorización a nivel de llamada generalmente se maneja mediante tokens que se aplican en metadatos cuando se realiza la llamada. Ejemplo de autenticación basada en token.
-
La autenticación a nivel de canal utiliza un certificado de cliente que se aplica a nivel de conexión. También puede incluir credenciales de autenticación/autorización a nivel de llamada para aplicarse automáticamente a cada llamada en el canal. Ejemplo de autenticación basada en certificado.
Se puede utilizar uno u otro de estos mecanismos o ambos para ayudar a proteger los servicios.
Middleware
En REST, utilizamos middleware para varios propósitos como:
-
Límites de tasa
-
Validación pre/post solicitud/respuesta
-
Abordar amenazas de seguridad
Podemos lograr lo mismo con gRPC también. La terminología es diferente en gRPC; se les conoce como interceptores, pero realizan actividades similares.
En la rama middleware del repositorio de código ‘greet_app’, hemos integrado interceptores de logger y Prometheus.
Mira cómo los interceptores están configurados para usar los paquetes de Prometheus y registro aquí.
Pero podemos integrar otros paquetes a los interceptores para fines como prevenir pánicos y recuperación (para manejar excepciones), trazado, incluso autenticación, y más.
Middleware soportado por el framework gRPC.
Empaquetado, Versionado y Prácticas de Código de Archivos Proto
Empaquetado
Sigamos la rama de empaquetado.
Primero, comienza con ‘Taskfile.yaml’, la tarea ‘gen-pkg’ indica ‘protoc –proto_path=packaging packaging/*.proto –go_out=packaging’. Esto significa que ‘protoc’ (el compilador) convertirá todos los archivos en ‘packaging/*.proto’ en sus equivalentes en ‘go’ como se indica con la bandera ‘–go_out=packaging’ en el directorio ‘packaging’ mismo.
En segundo lugar, en el archivo ‘processor.proto’, se han definido 2 mensajes: ‘CPU’ y ‘GPU’. Mientras que CPU es un mensaje simple con 3 campos de tipos de datos incorporados, GPU, por otro lado, tiene un tipo de dato personalizado llamado ‘Memory’. ‘Memory’ es un mensaje separado y está definido en un archivo diferente.
Entonces, ¿cómo usas el mensaje ‘Memory’ en el archivo ‘processor.proto’? Utilizando import.
Incluso si intentas generar un archivo proto ejecutando la tarea ‘gen-pkg’ después de mencionar import, arrojará un error. Ya que por defecto ‘protoc’ asume que ambos archivos ‘memory.proto’ y ‘processor.proto’ están en paquetes diferentes. Por lo tanto, necesitas mencionar el mismo nombre de paquete en ambos archivos.
El opcional ‘go_package’ indica al compilador que cree un nombre de paquete como ‘pb’ para los archivos en ‘go’. Si se crearan archivos proto para otro lenguaje, el nombre del paquete sería ‘laptop_pkg’.
Versionado
-
Pueden existir dos tipos de cambios en gRPC: cambios que rompen la compatibilidad y cambios que no lo hacen.
-
Los cambios que no rompen la compatibilidad incluyen agregar un nuevo servicio, agregar un nuevo método a un servicio, agregar un campo a la solicitud o respuesta proto, y agregar un valor a un enumerado
-
Cambios que rompen la compatibilidad, como renombrar un campo, cambiar el tipo de datos de un campo, el número de campo, renombrar o eliminar un paquete, servicio o métodos, requieren la versión de los servicios
-
Opcional empaquetado.
Prácticas de código
-
El mensaje de solicitud debe tener sufijo ‘request’, como `CreateUserRequest`
-
El mensaje de respuesta debe tener sufijo ‘response’, como `CreateUserResponse`
-
En caso de que el mensaje de respuesta esté vacío, puede utilizar un objeto vacío `CreateUserResponse` o usar el `google.protobuf.Empty`
-
El nombre del paquete debe tener sentido y debe estar versionado, por ejemplo: paquete `com.ic.internal_api.service1.v1`
Herramientas
El ecosistema gRPC soporta una variedad de herramientas para facilitar tareas no relacionadas con el desarrollo, como documentación, puerta de enlace REST para un servidor gRPC, integración de validadores personalizados, linting, etc. Aquí hay algunas herramientas que pueden ayudarnos a lograr lo mismo:
-
protoc-gen-grpc-gateway — plugin para crear una puerta de enlace REST API gRPC. Permite que los endpoints gRPC sean endpoints REST API y realiza la traducción de JSON a proto. Básicamente, define un servicio gRPC con algunas anotaciones personalizadas y hace que esos métodos gRPC sean accesibles a través de REST utilizando solicitudes JSON.
-
protoc-gen-swagger — un complemento para grpc-gateway. Es capaz de generar swagger.json basado en las anotaciones personalizadas requeridas para el gateway gRPC. Luego puedes importar ese archivo en tu cliente REST de elección (como Postman) y realizar llamadas de API REST a los métodos que has expuesto.
-
protoc-gen-grpc-web — un plugin que permite que nuestra interfaz frontal se comunique con el backend mediante llamadas gRPC. Una publicación de blog separada sobre esto en el futuro.
-
protoc-gen-go-validators — un complemento que permite definir reglas de validación para campos de mensajes proto. Genera un método de error Validate() para mensajes proto que puedes llamar en GoLang para validar si el mensaje coincide con tus expectativas predefinidas.
-
https://github.com/yoheimuta/protolint — un complemento para agregar reglas de linteo a archivos proto
Pruebas usando POSTMAN
A diferencia de probar API REST con POSTMAN o cualquier herramienta equivalente como Insomnia, no es muy cómodo probar servicios gRPC.
Nota: Los servicios gRPC también pueden probarse desde la CLI utilizando herramientas como evans-cli. Pero para ello es necesario habilitar la reflexión (si no está habilitada, se requiere la ruta al archivo proto) en los servidores gRPC. Cambios que deben realizarse para habilitar la reflexión y cómo ingresar al modo REPL de evans-cli. Después de ingresar al modo REPL de evans-cli, los servicios gRPC pueden probarse directamente desde la CLI y el proceso se describe en la página de GitHub de evans-cli.
Postman tiene una versión beta para probar servicios gRPC.
Aquí están los pasos de cómo puedes hacerlo:
-
Abrir Postman
-
Ir a ‘APIs’ en la barra lateral izquierda
-
Hacer clic en el signo ‘+’ para crear una nueva API:
-
En la ventana emergente, ingrese ‘Nombre’, ‘Versión’ y ‘Detalles del Esquema’ y haga clic en crear [a menos que necesite importar de algunas fuentes como github/bitbucket]. Este paso es relevante si deseas copiar y pegar el contrato proto.
5. Su API se crea como se muestra a continuación. Haga clic en la versión ‘1.0.0’, vaya a la definición y escriba su contrato proto.
-
Recuerde que la importación no funciona aquí, por lo que sería mejor mantener todos los protos dependientes en un solo lugar.
-
Los pasos anteriores ayudarán a retener los contratos para su uso futuro.
-
Luego haga clic en ‘Nuevo’ y seleccione ‘Solicitud gRPC’:
-
Ingrese el URI y elija el proto de la lista de APIs guardadas:
-
Ingrese su mensaje de solicitud y ‘Invocar’:
En los pasos anteriores, determinamos el proceso para probar nuestras API gRPC a través de POSTMAN. El proceso para probar los puntos finales gRPC es diferente al de los puntos finales REST utilizando POSTMAN. Una cosa a recordar es que al crear y guardar el contrato proto como en #5, todos los definiciones de mensajes y servicios proto deben estar en el mismo lugar. Ya que no hay una opción para acceder a los mensajes proto entre versiones en POSTMAN.
Conclusión
En esta publicación, desarrollamos una idea sobre RPC, trazamos paralelos con REST y también discutimos sus diferencias, luego nos adentramos en discutir una implementación de RPC, es decir, gRPC desarrollado por Google.
gRPC como marco de trabajo puede ser crucial, especialmente para la arquitectura basada en microservicios para la comunicación interna. Puede ser utilizado también para comunicación externa, pero requerirá una puerta de enlace REST. gRPC es esencial para aplicaciones de streaming y en tiempo real.
La forma en que Golang se está consolidando como lenguaje de scripting del lado del servidor, gRPC se está consolidando como marco de comunicación de facto.
Source:
https://dzone.com/articles/understanding-grpc-concepts-use-cases-amp-best-pra