Patrones de Diseño para Microservicios
Patrones de Diseño para Microservicios
Mary y su equipo, como la mayoría de los otros desarrolladores, tenían cierta experiencia con
los mecanismos de comunicación entre procesos (IPC). La aplicación FTGO tiene una API REST
que utilizan las aplicaciones móviles y JavaScript del lado del navegador.
También utiliza varios servicios en la nube, como el servicio de mensajería Twilio y el servicio
de pago Stripe.
Pero dentro de una aplicación monolítica como FTGO, los módulos se invocan entre sí
mediante métodos de nivel de lenguaje o llamadas a funciones. Los desarrolladores de FTGO
generalmente no necesitan pensar en IPC a menos que estén trabajando en la API REST o en
los módulos que se integran con los servicios en la nube.
No hay escasez de mecanismos de IPC para elegir. Hoy, la opción de moda es REST (con JSON).
Sin embargo, es importante recordar que no hay balas de plata. Debes considerar
cuidadosamente las opciones. Este capítulo explora varias opciones de IPC, incluyendo REST y
mensajes, y discute las compensaciones.
La elección del mecanismo de IPC es una decisión arquitectónica importante. Puede afectar la
disponibilidad de la aplicación. Además, como explico en este capítulo y en el siguiente, IPC
incluso se cruza con la administración de transacciones. Estoy a favor de una arquitectura que
consista en servicios acoplados libremente que se comuniquen entre sí mediante mensajería
asincrónica. Los protocolos síncronos como REST se utilizan principalmente para comunicarse
con otras aplicaciones.
Comienzo este capítulo con una descripción general de la comunicación entre procesos en la
arquitectura de microservicios. A continuación, describo el procedimiento remoto basado en
invocación IPC, de los cuales REST es el ejemplo más popular. Cubro temas importantes,
incluido el descubrimiento de servicios y cómo manejar fallas parciales. Después de eso,
describo la IPC basada en mensajería asincrónica. También hablo sobre escalar a los
consumidores mientras se preserva el orden de los mensajes, se manejan correctamente los
mensajes duplicados y los mensajes transaccionales. Finalmente, paso por el concepto de
servicios autónomos que manejan solicitudes síncronas sin comunicarse con otros servicios
para mejorar la disponibilidad.
Hay muchas tecnologías IPC diferentes para elegir. Los servicios pueden usar
mecanismos de comunicación síncronos basados en solicitudes / respuestas, como
REST basado en HTTP o gRPC. Alternativamente, pueden usar mecanismos de
comunicación asíncronos basados en mensajes como AMQP o STOMP. También hay
una variedad de formatos de mensajes diferentes. Los servicios pueden utilizar formatos
de texto legibles para humanos, como JSON o XML. Alternativamente, podrían usar un
formato binario más eficiente como Avro o Protocol Buffers.
Antes de entrar en detalles de tecnologías específicas, quiero presentar varios problemas
de diseño que debe considerar. Comienzo esta sección con una discusión sobre los
estilos de interacción, que son una forma independiente de la tecnología de describir
cómo interactúan los clientes y los servicios. A continuación, analizo la importancia de
definir con precisión las API en una arquitectura de microservicios, incluido el concepto
de diseño API primero. Después de eso, discuto el importante tema de la evolución de la
API. Finalmente, analizo diferentes opciones para los formatos de mensajes y cómo
pueden determinar la facilidad de evolución de la API. Comencemos mirando los estilos
de interacción.
3.1.1 Estilos de interacción
Es útil pensar primero en el estilo de interacción entre un servicio y sus clientes antes de
seleccionar un mecanismo de IPC para la API de un servicio. Pensar primero en el estilo
de interacción lo ayudará a enfocarse en los requisitos y evitar enredarse en los detalles
de una tecnología IPC particular. Además, como se describe en la sección 3.4, la
elección del estilo de interacción afecta la disponibilidad de su aplicación. Además,
como verá en los capítulos 9 y 10, le ayuda a seleccionar la estrategia de prueba de
integración adecuada.
Hay una variedad de estilos de interacción cliente-servicio. Como muestra la tabla 3.1,
se pueden clasificar en dos dimensiones. La primera dimensión es si la interacción es
uno a uno o uno a muchos:
Uno a uno: cada solicitud de cliente es procesada por exactamente un servicio.
Uno a muchos: cada solicitud es procesada por múltiples servicios.
La segunda dimensión es si la interacción es síncrona o asíncrona:
Sincrónico: el cliente espera una respuesta oportuna del servicio e incluso puede
bloquear mientras espera.
Asíncrono: el cliente no bloquea y la respuesta, si la hay, no se envía necesariamente de
inmediato.
Tabla 3.1 Los diversos estilos de interacción se pueden caracterizar en dos dimensiones:
uno a uno frente a uno a mano y sincrónico frente a asíncrono.
Uno a uno Uno a muchos
Sincrónico Solicitud/respuesta ---
Asincrónico Solicitud/respuesta Publicar/suscribirse
sincrónica. Publicar/responder
Notificaciones asíncronas
unidireccionales
Las API o interfaces son fundamentales para el desarrollo de software. Una aplicación se
compone de módulos. Cada módulo tiene una interfaz que define el conjunto de operaciones
que los clientes del módulo pueden invocar. Una interfaz bien diseñada expone
funcionalidades útiles mientras oculta la implementación. Permite que la implementación
cambie sin afectar a los clientes.
Además, debido a que Java es un lenguaje de tipo estático, si la interfaz cambia para ser
incompatible con el cliente, la aplicación no se compilará.
Las API y las interfaces son igualmente importantes en una arquitectura de microservicios. La
API de un servicio es un contrato entre el servicio y sus clientes. Como se describe en el
capítulo 2, la API de un servicio consta de operaciones, que los clientes pueden invocar, y
eventos, que son publicados por el servicio. Una operación tiene un nombre, parámetros y un
tipo de retorno. Un evento tiene un tipo y un conjunto de campos y, como se describe en la
sección 3.3, se publica en un canal de mensajes.
El desafío es que una API de servicio no se define utilizando una construcción de lenguaje de
programación simple. Por definición, un servicio y sus clientes no se compilan juntos. Si se
implementa una nueva versión de un servicio con una API incompatible, no hay compilación
Independientemente del mecanismo IPC que elija, es importante definir con precisión la API de
un servicio utilizando algún tipo de lenguaje de definición de interfaz (IDL). Además, existen
buenos argumentos para usar un enfoque API primero para definir servicios (ver
www.programmableweb.com/news/how-to-design-great-apis-api-first-design-and-raml/how-
to/ 10/07/2015 para más). Primero escribe la definición de la interfaz. Luego revisa la
definición de interfaz con los desarrolladores del cliente. Solo después de iterar en la definición
de API puede implementar el servicio. Hacer este diseño inicial aumenta sus posibilidades de
construir un servicio que satisfaga las necesidades de sus clientes.
La naturaleza de la definición de API depende del mecanismo de IPC que esté utilizando. Por
ejemplo, si usa mensajes, la API consta de los canales de mensajes, los tipos de mensajes y los
formatos de mensajes. Si está utilizando HTTP, la API consta de las URL, los verbos HTTP y los
formatos de solicitud y respuesta. Más adelante en este capítulo, explico cómo definir las API.
En una aplicación basada en microservicios, cambiar la API de un servicio es mucho más difícil.
Los clientes de un servicio son otros servicios, que a menudo son desarrollados por otros
equipos.
Los clientes pueden incluso ser otras aplicaciones fuera de la organización. Por lo general, no
puede obligar a todos los clientes a actualizarse al mismo tiempo que el servicio. Además,
debido a que las aplicaciones modernas generalmente no están inactivas por mantenimiento,
normalmente realizará una actualización continua de su servicio, por lo que las versiones
antiguas y nuevas de un servicio se ejecutarán simultáneamente.
Es importante tener una estrategia para enfrentar estos desafíos. La forma en que maneja un
cambio en una API depende de la naturaleza del cambio.
La especificación de versiones semánticas (http://semver.org) es una guía útil para las API de
versiones. Es un conjunto de reglas que especifican cómo se usan e incrementan los números
de versión. El control de versiones semántico fue originalmente diseñado para el control de
versiones de paquetes de software, pero puede usarlo para el control de versiones de API en
un sistema distribuido.
Hay un par de lugares donde puede usar el número de versión en una API. Si está
implementando una API REST, puede, como se menciona a continuación, usar la versión
principal como primer elemento de la ruta URL. Alternativamente, si está implementando un
servicio que usa mensajería, puede incluir el número de versión en los mensajes que publica. El
objetivo es versionar adecuadamente las API y evolucionarlas de forma controlada. Veamos
cómo manejar cambios menores y mayores.
Idealmente, debe esforzarse por hacer solo cambios compatibles con versiones anteriores. Los
cambios compatibles con versiones anteriores son cambios aditivos en una API:
A veces debe realizar cambios importantes e incompatibles en una API. Debido a que no puede
obligar a los clientes a actualizar de inmediato, un servicio debe admitir simultáneamente
versiones antiguas y nuevas de una API durante un período de tiempo. Si está utilizando un
mecanismo de IPC basado en HTTP, como REST, un enfoque es incrustar el número de versión
principal en la URL. Por ejemplo, las rutas de la versión 1 tienen el prefijo '/ v1 / ...' y las rutas
de la versión 2 con '/ v2 / ...'.
Esta solicitud le dice al Servicio de pedidos que el cliente espera una respuesta de la versión
1.x. Para admitir múltiples versiones de una API, los adaptadores del servicio que implementan
las API contendrán lógica que se traduce entre las versiones antigua y nueva.
Ahora veremos el tema de los formatos de mensajes, cuya elección puede afectar la facilidad
de evolución de una API.
Hay dos categorías principales de formatos de mensaje: texto y binario. Echemos un vistazo a
cada uno.
Una desventaja de usar un formato de mensajes basado en texto es que los mensajes tienden
a ser detallados, especialmente XML. Cada mensaje tiene la sobrecarga de contener los
nombres de los atributos además de sus valores. Otro inconveniente es la sobrecarga del
análisis de texto, especialmente cuando los mensajes son grandes. En consecuencia, si la
eficiencia y el rendimiento son importantes, puede considerar usar un formato binario.
Hay varios formatos binarios diferentes para elegir. Los formatos populares incluyen Protocol
Buffers (https://developers.google.com/protocol-buffers/docs/overview) y Avro
(https://avro.apache.org). Ambos formatos proporcionan un IDL escrito para definir la
estructura de sus mensajes. Un compilador genera el código que serializa y deserializa los
mensajes. ¡Estás obligado a adoptar un enfoque de API primero para el diseño de servicios!
Además, si escribe a su cliente en un lenguaje de tipo estático, el compilador verifica que usa la
API correctamente.
Una diferencia entre estos dos formatos binarios es que Protocol Buffers usa campos
etiquetados, mientras que un consumidor de Avro necesita conocer el esquema para
interpretar los mensajes. Como resultado, manejar la evolución de la API es más fácil con
Protocol Buffers que con Avro. Esta publicación de blog
(http://martin.kleppmann.com/2012/12/05/schemaevolution-in avro-protocol-buffers-
thrift.html) es una excelente comparación de Thrift, Protocol Buffers y Avro.
Ahora que hemos visto los formatos de mensajes, veamos los mecanismos específicos de IPC
que transportan los mensajes, comenzando con el patrón de invocación de procedimiento
remoto (RPI).
La Figura 3.1 muestra cómo funciona RPI. La lógica de negocio en el cliente invoca una interfaz
proxy, implementada por una clase de adaptador proxy RPI. El proxy RPI realiza una solicitud al
servicio. La solicitud es manejada por una clase de adaptador de servidor RPI, que invoca la
lógica de negocio del servicio a través de una interfaz. Luego envía una respuesta al proxy RPI,
que devuelve el resultado a la lógica de negocio del cliente.
messaging.html).
Existen numerosos protocolos para elegir. En esta sección, describo REST y gRPC. Cubro cómo
mejorar la disponibilidad de sus servicios mediante el manejo adecuado de fallas parciales y
explicar por qué una aplicación basada en microservicios que usa RPI debe usar un mecanismo
de descubrimiento de servicios.
www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
Muchos desarrolladores afirman que sus API basadas en HTTP son RESTful. Pero como Roy
Fielding describe en una publicación de blog, no todos ellos son realmente
(http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext impulsado). Para entender
por qué, echemos un vistazo al modelo de madurez REST.
Leonard Richardson (sin relación con su autor) define un modelo de madurez muy útil para
REST (http://martinfowler.com/articles/richardsonMaturityModel.html) que consta de los
siguientes niveles:
Le recomiendo que revise las API REST en su organización para ver a qué nivel corresponden.
Los recursos de REST suelen estar orientados a objetos comerciales, como el consumidor y el
pedido. En consecuencia, un problema común al diseñar una API REST es cómo habilitar al
cliente para recuperar múltiples objetos relacionados en una sola solicitud. Por ejemplo,
imagine que un cliente REST quería recuperar un pedido y el consumidor del pedido. Una API
REST pura requeriría que el cliente haga al menos dos solicitudes, una para el Pedido y otra
para su Consumidor. Un escenario más complejo requeriría aún más viajes de ida y vuelta y
sufriría una latencia excesiva.
Una solución a este problema es que una API permita al cliente recuperar recursos
relacionados cuando obtiene un recurso. Por ejemplo, un cliente podría recuperar un Pedido y
su Consumidor usando GET / orders / order-id-1345? Expand = consumer. El parámetro de
consulta especifica los recursos relacionados a devolver con la Orden. Este enfoque funciona
bien en muchos escenarios, pero a menudo es insuficiente para escenarios más complejos.
También lleva mucho tiempo implementarlo. Esto ha llevado a la creciente popularidad de
tecnologías API alternativas como GraphQL (http://graphql.org) y Netflix Falcor
(http://netflix.github.io/falcor/), que están diseñadas para admitir la obtención eficiente de
datos.
Otro problema común de diseño de la API REST es cómo asignar las operaciones que desea
realizar en un objeto comercial a un verbo HTTP. Una API REST debe usar PUT para
actualizaciones, pero puede haber múltiples formas de actualizar un pedido, incluida la
cancelación, revisión del pedido, etc. Además, una actualización podría no ser idempotente, lo
cual es un requisito para usar PUT. Una solución es definir un subrecurso para actualizar un
aspecto particular de un recurso. El Servicio de pedidos, por ejemplo, tiene un POST / orders /
{orderId} / cancel endpoint para cancelar pedidos, y un POST / orders / {orderId} / revise
endpoint para revisar pedidos. Otra solución es especificar un verbo como parámetro de
consulta de URL. Lamentablemente, ninguna de las soluciones es particularmente RESTful.
Este problema con las operaciones de mapeo a los verbos HTTP ha llevado a la creciente
popularidad de las alternativas a REST, como gPRC, discutido en breve en la sección 3.2.2. Pero
primero veamos los beneficios y los inconvenientes de REST.
Es simple y familiar.
Puede probar una API HTTP desde un navegador usando, por ejemplo, el
complemento Postman, o desde la línea de comandos usando curl (suponiendo que se
use JSON u otro formato de texto).
A pesar de estos inconvenientes, REST parece ser el estándar de facto para las API, aunque hay
un par de alternativas interesantes. GraphQL, por ejemplo, implementa la obtención de datos
flexible y eficiente. El Capítulo 8 analiza GraphQL y cubre el patrón de API Gateway.
Como se mencionó en la sección anterior, un desafío con el uso de REST es que debido a que
HTTP solo proporciona un número limitado de verbos, no siempre es sencillo diseñar una API
REST que admita múltiples operaciones de actualización. Una tecnología IPC que evita este
problema es gRPC (www.grpc.io), un marco para escribir clientes y servidores en varios
idiomas (consulte https://en.wikipedia.org/wiki/Remote_procedure_call para obtener más
información). gRPC es un protocolo basado en mensajes binarios, y esto significa, como se
mencionó anteriormente en la discusión de los formatos de mensajes binarios, que se ve
obligado a adoptar un enfoque API primero para el diseño de servicios. Usted define sus API de
gRPC utilizando un IDL basado en Protocol Buffers, que es el mecanismo de lenguaje neutral de
Google para serializar datos estructurados. Utiliza el compilador Protocol Buffer para generar
stubs del lado del cliente y esqueletos del lado del servidor. El compilador puede generar
código para una variedad de lenguajes, incluidos Java, C #, NodeJS y GoLang. Los clientes y
servidores intercambian mensajes binarios en el formato de Protocol Buffers usando HTTP/2.
Una API de gRPC consta de uno o más servicios y definiciones de mensajes de solicitud /
respuesta. Una definición de servicio es análoga a una interfaz Java y es una colección de
métodos fuertemente tipados. Además de admitir RPC de solicitud / respuesta simple, gRPC
admite la transmisión de RPC. Un servidor puede responder con una secuencia de mensajes al
cliente. Alternativamente, un cliente puede enviar una secuencia de mensajes al servidor.
gRPC usa Protocol Buffers como formato de mensaje. Protocol Buffers es, como se mencionó
anteriormente, un formato binario eficiente, compacto. Es un formato etiquetado. Cada
campo de un mensaje de Protocol Buffers está numerado y tiene un código de tipo. Un
destinatario del mensaje puede extraer los campos que necesita y omitir los campos que no
reconoce. Como resultado, gRPC permite que las API evolucionen sin dejar de ser compatibles
con versiones anteriores.
El Listado 3.1 muestra un extracto de la API de gRPC para el Servicio de pedidos. Define varios
métodos, incluido createOrder (). Este método toma un CreateOrderRequest como parámetro
y devuelve un CreateOrderReply.
Se necesita más trabajo para que los clientes de JavaScript consuman API basada en
gRPC que las API basadas en REST / JSON.
Los firewalls más antiguos podrían no ser compatibles con HTTP / 2.
gRPC es una alternativa convincente a REST, pero al igual que REST, es un mecanismo de
comunicación síncrono, por lo que también sufre el problema del fallo parcial. Echemos un
vistazo a qué es eso y cómo manejarlo.
En un sistema distribuido, cada vez que un servicio realiza una solicitud sincrónica a otro
servicio, existe un riesgo siempre presente de falla parcial. Debido a que el cliente y el servicio
son procesos separados, es posible que un servicio no pueda responder de manera oportuna a
la solicitud de un cliente. El servicio podría estar inactivo debido a una falla o por
mantenimiento. O el servicio podría estar sobrecargado y responder extremadamente lento a
las solicitudes.
Debido a que el cliente está bloqueado a la espera de una respuesta, el peligro es que la falla
podría caer en cascada a los clientes del cliente y así sucesivamente y causar una interrupción.
Un proxy RPI que rechaza inmediatamente las invocaciones por un período de tiempo de
espera después de que el número de fallas consecutivas exceda un umbral especificado. Ver
http://microservices.io/patterns/reliability/circuit-breaker.html.
Considere, por ejemplo, el escenario que se muestra en la figura 3.2, donde el Servicio de
pedidos no responde. Un cliente móvil realiza una solicitud REST a una API Gateway, que,
como se discutió en el capítulo 8, es el punto de entrada a la aplicación para clientes API. La
API Gateway envía la solicitud al servicio de pedidos que no responde.
Figura 3.2 Una API Gateway debe protegerse de los servicios que no responden, como el
Servicio de pedidos.
Es esencial que diseñe sus servicios para evitar fallas parciales en cascada en toda la aplicación.
La solución tiene dos partes:
Debe usar proxies RPI de diseño, como OrderServiceProxy, para manejar servicios
remotos que no responden.
Debe decidir cómo recuperarse de un servicio remoto fallido.
Cada vez que un servicio invoca sincrónicamente otro servicio, debe protegerse utilizando el
enfoque descrito por Netflix (http://techblog.netflix.com/2012/02/faulttolerance-in-high
volume.html). Este enfoque consiste en una combinación de los siguientes mecanismos:
Tiempos de espera de red: nunca bloquee indefinidamente y siempre use tiempos de espera
cuando espere una respuesta. El uso de tiempos de espera garantiza que los recursos nunca se
inmovilicen indefinidamente.
Usar una biblioteca como Hystrix es solo una parte de la solución. También debe decidir caso
por caso cómo sus servicios deberían recuperarse de un servicio remoto que no responde. Una
opción es que un servicio simplemente devuelva un error a su cliente. Por ejemplo, este
enfoque tiene sentido para el escenario que se muestra en la figura 3.2, donde falla la solicitud
de crear una Orden. La única opción es que la puerta de enlace API devuelva un error al cliente
móvil.
En otros escenarios, puede tener sentido devolver un valor de reserva, como un valor
predeterminado o una respuesta en caché. Por ejemplo, el capítulo 7 describe cómo la API
Gateway podría implementar la operación de consulta findOrder() utilizando el patrón de
composición API. Como muestra la figura 3.3, su implementación del punto final GET / orders /
{orderId} invoca varios servicios, incluidos el Servicio de pedidos, el Servicio de cocina y
Es probable que los datos de cada servicio no sean igualmente importantes para el cliente. Los
datos del Servicio de pedidos son esenciales. Si este servicio no está disponible, la API Gateway
debería devolver una versión en caché de sus datos o un error. Los datos de los otros servicios
son menos críticos. Un cliente puede, por ejemplo, mostrar información útil al usuario incluso
si el estado de entrega no estaba disponible. Si el Servicio de entrega no está disponible, la
puerta de enlace API debe devolver una versión en caché de sus datos u omitirla de la
respuesta.
Es esencial que diseñe sus servicios para manejar fallas parciales, pero ese no es el único
problema que debe resolver al usar RPI. Otro problema es que para que un servicio invoque
otro servicio usando RPI, necesita conocer la ubicación de red de una instancia de servicio. En
la superficie, esto suena simple, pero en la práctica es un problema desafiante. Debe usar un
mecanismo de descubrimiento de servicios. Veamos cómo funciona eso.
Figura 3.3 La API Gateway implementa el punto final GET / orders / {orderId} utilizando la
composición API. Llama a varios servicios, agrega sus respuestas y envía una respuesta a la
aplicación móvil. El código que implementa el punto final debe tener una estrategia para
manejar la falla de cada servicio que llama.
Supongamos que está escribiendo un código que invoca un servicio que tiene una API REST.
Para realizar una solicitud, su código debe conocer la ubicación de la red (dirección IP y puerto)
de una instancia de servicio. En una aplicación tradicional que se ejecuta en hardware físico,
las ubicaciones de red de las instancias de servicio suelen ser estáticas. Por ejemplo, su código
podría leer las ubicaciones de red de un archivo de configuración que se actualiza
ocasionalmente. Pero en una aplicación moderna de microservicios basada en la nube,
generalmente no es tan simple. Como se muestra en la figura 3.4, una aplicación moderna es
mucho más dinámica.
Las instancias de servicio tienen ubicaciones de red asignadas dinámicamente. Además, el
conjunto de instancias de servicio cambia dinámicamente debido a escalado automático, fallas
y actualizaciones. En consecuencia, su código de cliente debe usar un descubrimiento de
servicio.
Como acaba de ver, no puede configurar estáticamente un cliente con las direcciones IP de los
servicios. En cambio, una aplicación debe usar un mecanismo de descubrimiento de servicio
dinámico. El descubrimiento de servicios es conceptualmente bastante simple: su componente
clave es un registro de servicios, que es una base de datos de las ubicaciones de red de las
instancias de servicio de una aplicación.
Figura 3.5 El registro del servicio realiza un seguimiento de las instancias del servicio. Los
clientes consultan el registro del servicio para encontrar ubicaciones de red de instancias de
servicio disponibles.
El segundo patrón es el patrón de descubrimiento del lado del cliente. Cuando un cliente del
servicio desea invocar un servicio, consulta el registro del servicio para obtener una lista de las
instancias del servicio. Para mejorar el rendimiento, un cliente puede almacenar en caché las
instancias del servicio. El cliente del servicio luego utiliza un algoritmo de equilibrio de carga,
como un round-robin o aleatorio, para seleccionar una instancia de servicio. Luego realiza una
solicitud a una instancia de servicio seleccionada.
Una ventaja del descubrimiento de servicios a nivel de aplicación es que maneja el escenario
cuando los servicios se implementan en múltiples plataformas de implementación. Imagine,
por ejemplo, que ha implementado solo algunos de los servicios en Kubernetes, discutidos en
el capítulo 12, y el resto se ejecuta en un entorno heredado. El descubrimiento de servicios a
nivel de aplicación utilizando Eureka, por ejemplo, funciona en ambos entornos, mientras que
el descubrimiento de servicios basado en Kubernetes solo funciona dentro de Kubernetes.
Un cliente realiza una solicitud a un enrutador, que es responsable del descubrimiento del
servicio. Ver http://microservices.io/patterns/server-side-discovery.html.
Ahora que hemos analizado el IPC sincrónico usando REST o gRPC, echemos un vistazo a la
alternativa: comunicación asincrónica basada en mensajes.
Patrón: mensajería
Comienzo esta sección con una descripción general de la mensajería. Muestro cómo
describir una arquitectura de mensajería independientemente de la tecnología de
mensajería. A continuación, comparo y contrasto las arquitecturas sin intermediario y
basadas en intermediario y describo los criterios para seleccionar un intermediario de
mensajes. Luego discuto varios temas importantes, incluyendo el escalado de los
consumidores mientras se preserva el pedido de mensajes, la detección y el descarte de
mensajes duplicados, y el envío y recepción de mensajes como parte de una transacción
de base de datos. Comencemos mirando cómo funciona la mensajería.