0% encontró este documento útil (0 votos)
541 vistas21 páginas

Patrones de Diseño para Microservicios

Este documento discute varios temas relacionados con la comunicación entre procesos en una arquitectura de microservicios, incluidos los diferentes estilos de interacción, la importancia de definir las API, las opciones de mecanismos de comunicación como REST y mensajería, y cómo estas decisiones afectan la disponibilidad y la evolución de la aplicación. Explica que la comunicación entre procesos desempeña un papel más importante en una arquitectura de microservicios que en una aplicación monolítica debido a que los servicios suelen ejec

Cargado por

mikipmax
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
541 vistas21 páginas

Patrones de Diseño para Microservicios

Este documento discute varios temas relacionados con la comunicación entre procesos en una arquitectura de microservicios, incluidos los diferentes estilos de interacción, la importancia de definir las API, las opciones de mecanismos de comunicación como REST y mensajería, y cómo estas decisiones afectan la disponibilidad y la evolución de la aplicación. Explica que la comunicación entre procesos desempeña un papel más importante en una arquitectura de microservicios que en una aplicación monolítica debido a que los servicios suelen ejec

Cargado por

mikipmax
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd

Comunicación entre procesos en una arquitectura de microservicios

Este capítulo cubre:

 Aplicando los patrones de comunicación: Invocación remota de procedimientos,


disyuntor (Circuit breaker), descubrimiento del lado del cliente, auto registro,
descubrimiento del lado del servidor, registro de terceros, mensajería asincrónica,
bandeja de salida transaccional, registro de transacciones, editor de sondeo.
 La importancia de la comunicación entre procesos en una arquitectura de
microservicios.
 Definición y evolución de las API.
 Las diversas opciones de comunicación entre procesos y sus compensaciones.
 Los beneficios de los servicios que se comunican mediante mensajería asincrónica.
 Envío confiable de mensajes como parte de una transacción de base de datos

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.

En contraste, como se vio en el capítulo 2, la arquitectura de microservicios estructura una


aplicación como un conjunto de servicios. Esos servicios a menudo deben colaborar para
manejar una solicitud. Debido a que las instancias de servicio suelen ser procesos que se
ejecutan en varias máquinas, deben interactuar utilizando IPC. Desempeña un papel mucho
más importante en una arquitectura de microservicios que en una aplicación monolítica. En
consecuencia, a medida que migran su aplicación a microservicios, Mary y el resto de los
desarrolladores de FTGO necesitarán pasar mucho más tiempo pensando en IPC.

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.

3.1 Descripción general de la comunicación entre procesos en una arquitectura de


microservicio

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

Los siguientes son los diferentes tipos de interacciones uno a uno:

 Solicitud/respuesta: un cliente de servicio realiza una solicitud a un servicio y espera


una respuesta. El cliente espera que la respuesta llegue de manera oportuna. Es
posible que se bloquee mientras espera. Este es un estilo de interacción que
generalmente da como resultado servicios estrechamente acoplados.
 Solicitud/respuesta asincrónica: un cliente de servicio envía una solicitud a un servicio,
que responde de forma asincrónica. El cliente no bloquea mientras espera, porque el
servicio podría no enviar la respuesta durante mucho tiempo.
 Notificaciones unidireccionales: un cliente de servicio envía una solicitud a un servicio,
pero no se espera ni se envía respuesta.

Es importante recordar que el estilo de interacción síncrona de solicitud / respuesta es


mayormente ortogonal a las tecnologías IPC. Un servicio puede, por ejemplo, interactuar
con otro servicio utilizando la interacción de estilo de solicitud / respuesta con REST o
mensajería. Incluso si dos servicios se comunican mediante un intermediario de mensajes,
el servicio del cliente podría bloquearse en espera de una respuesta. No significa
necesariamente que estén débilmente acoplados. Es algo que reviso más adelante en este
capítulo cuando analizo el impacto de la comunicación entre servicios en la disponibilidad.

Los siguientes son los diferentes tipos de interacciones uno a muchos:

 Publicar/suscribirse: un cliente publica un mensaje de notificación, que es consumido


por cero o más servicios interesados.
 Publicar/respuestas asíncronas: un cliente publica un mensaje de solicitud y luego
espera un cierto tiempo para recibir respuestas de los servicios interesados.

Cada servicio generalmente usará una combinación de estos estilos de interacción.


Muchos de los servicios en la aplicación FTGO tienen API síncronas y asíncronas para
operaciones, y muchos también publican eventos.

Veamos cómo definir la API de un servicio.

3.1.2 Definición de API en una arquitectura de microservicio

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.

En una aplicación monolítica, una interfaz generalmente se especifica utilizando una


construcción de lenguaje de programación como una interfaz Java. Una interfaz Java especifica
un conjunto de métodos que un cliente puede invocar. La clase de implementación está oculta
para el cliente.

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

error. En cambio, habrá fallas de tiempo de ejecució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.

El diseño API primero es esencial

Incluso en proyectos pequeños, he visto problemas porque los componentes no están de


acuerdo con una API. Por ejemplo, en un proyecto, el desarrollador de back-end de Java y el
desarrollador de interfaz de AngularJS dijeron que habían completado el desarrollo. La
aplicación, sin embargo, no funcionó. La API REST y WebSocket utilizada por la aplicación
frontend para comunicarse con el backend estaba mal definida. Como resultado, las dos
aplicaciones no pudieron comunicarse.

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.

La API de un servicio rara vez se establece en piedra. Probablemente evolucionará con el


tiempo. Echemos un vistazo a cómo hacerlo y consideremos los problemas que enfrentará.

3.1.3 API en evolución


Las API cambian invariablemente con el tiempo a medida que se agregan nuevas funciones, se
modifican las funciones existentes y (quizás) se eliminan las funciones antiguas. En una
aplicación monolítica, es relativamente sencillo cambiar una API y actualizar a todos los callers.
Si está utilizando un lenguaje de tipo estático, el compilador ayuda al proporcionar una lista de
errores de compilación. El único desafío puede ser el alcance del cambio. Puede llevar mucho
tiempo cambiar una API ampliamente utilizada.

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.

UTILICE VERSIONES SEMANTICAS

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.

La especificación de versiones semánticas (Semvers) requiere un número de versión que


consta de tres partes: MAJOR.MINOR.PATCH. Debe incrementar cada parte de un número de
versión de la siguiente manera:

 MAJOR: cuando realiza un cambio incompatible en la API


 MINOR: cuando realiza mejoras compatibles con versiones anteriores de la API
 PATCH: cuando realiza una corrección de errores compatible con versiones anteriores

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.

REALIZANDO CAMBIOS MENORES Y COMPATIBLES

Idealmente, debe esforzarse por hacer solo cambios compatibles con versiones anteriores. Los
cambios compatibles con versiones anteriores son cambios aditivos en una API:

 Agregar atributos opcionales para request


 Agregar atributos a una response
 Agregar nuevas operaciones
Si solo realiza este tipo de cambios, los clientes mayores trabajarán con servicios más nuevos,
siempre que respeten el principio de Robustez
(https://en.wikipedia.org/wiki/Robustness_principle), que establece: “Sea conservador en lo
que hazlo, sé liberal en lo que aceptas de los demás ”. Los servicios deben proporcionar valores
predeterminados para los atributos de solicitud faltantes. Del mismo modo, los clientes deben
ignorar cualquier atributo de respuesta adicional. Para que esto sea sencillo, los clientes y los
servicios deben usar un formato de solicitud y respuesta que admita el principio de Robustez.
Más adelante en esta sección, describo cómo los formatos basados en texto como JSON y XML
generalmente facilitan la evolución de las API.

HACIENDO MAJOR, ROMPIENDO CAMBIOS

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 / ...'.

Otra opción es utilizar el mecanismo de negociación de contenido de HTTP e incluir el número


de versión en el tipo MIME. Por ejemplo, un cliente solicitaría la versión 1.x de un Pedido
usando una solicitud como esta:

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.

Además, como se describe en el capítulo 8, la API Gateway seguramente utilizará APIs


versionadas. Incluso puede tener que admitir numerosas versiones anteriores de una API.

Ahora veremos el tema de los formatos de mensajes, cuya elección puede afectar la facilidad
de evolución de una API.

3.1.4 Formatos de mensaje

La esencia de IPC es el intercambio de mensajes. Los mensajes generalmente contienen datos,


por lo que una decisión de diseño importante es el formato de esos datos. La elección del
formato del mensaje puede afectar la eficiencia de IPC, la usabilidad de la API y su capacidad
de evolución. Si está utilizando un sistema de mensajería o protocolos como HTTP, puede
elegir el formato de su mensaje. Algunos mecanismos de IPC, como gRPC, del cual aprenderá
en breve, pueden dictar el formato del mensaje. En cualquier caso, es esencial utilizar un
formato de mensaje en varios idiomas. Incluso si hoy escribe sus microservicios en un solo
idioma, es probable que utilice otros idiomas en el futuro. No debe, por ejemplo, utilizar la
serialización Java.

Hay dos categorías principales de formatos de mensaje: texto y binario. Echemos un vistazo a
cada uno.

FORMATOS DE MENSAJES BASADOS EN TEXTO


La primera categoría es formatos basados en texto como JSON y XML. Una ventaja de estos
formatos es que no solo son legibles para los humanos, sino que se describen a sí mismos. Un
mensaje JSON es una colección de propiedades con nombre. Del mismo modo, un mensaje
XML es efectivamente una colección de elementos y valores con nombre. Este formato
permite al consumidor de un mensaje seleccionar los valores de interés e ignorar el resto. En
consecuencia, muchos cambios en el esquema del mensaje pueden ser fácilmente compatibles
con versiones anteriores.

La estructura de los documentos XML se especifica mediante un esquema XML


(www.w3.org/XML/Schema). Con el tiempo, la comunidad de desarrolladores se ha dado
cuenta de que JSON también necesita un mecanismo similar. Una opción popular es usar el
estándar de esquema JSON

(http://json-schema.org). Un esquema JSON define los nombres y tipos de propiedades de un


mensaje y si son opcionales u obligatorios. Además de ser una documentación útil, una
aplicación puede utilizar un esquema JSON para validar los mensajes entrantes.

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.

FORMATOS DE MENSAJES BINARIOS

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).

3.2 Comunicación utilizando el patrón de invocación de procedimiento remoto síncrono

Cuando se utiliza un mecanismo de IPC basado en invocación de procedimiento remoto, un


cliente envía una solicitud a un servicio, y el servicio procesa la solicitud y devuelve una
respuesta.
Algunos clientes pueden bloquear la espera de una respuesta, y otros pueden tener una
arquitectura reactiva y sin bloqueo. Pero a diferencia del uso de mensajes, el cliente supone
que la respuesta llegará de manera oportuna.

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.

Patrón: invocación de procedimiento remoto.

Un cliente invoca un servicio utilizando un protocolo sincrónico basado en invocación de


procedimiento remoto, como REST (http://microservices.io/patterns/communication-style/

messaging.html).

La interfaz proxy generalmente encapsula el protocolo de comunicación subyacente.

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.

Primero echemos un vistazo a REST.

Comunicación utilizando el patrón de invocación de procedimiento remoto síncrono

3.2.1 Uso de REST

Hoy, está de moda desarrollar API en el estilo RESTful


(https://en.wikipedia.org/wiki/Representational_state_transfer). REST es un mecanismo de
IPC que (casi siempre) usa HTTP. Roy Fielding, el creador de REST, define REST de la siguiente
manera:
REST proporciona un conjunto de restricciones arquitectónicas que, cuando se aplican como
un todo, enfatizan la escalabilidad de las interacciones de los componentes, la generalidad de
las interfaces, la implementación independiente de los componentes y los componentes
intermedios para reducir la latencia de interacción, reforzar la seguridad y encapsular los
sistemas heredados.

www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

Un concepto clave en REST es un recurso, que generalmente representa un único objeto


comercial, como un Cliente o Producto, o una colección de objetos comerciales. REST utiliza los
verbos HTTP para manipular recursos, a los que se hace referencia mediante una URL. Por
ejemplo, una solicitud GET devuelve la representación de un recurso, que a menudo tiene la
forma de un documento XML o un objeto JSON, aunque se pueden usar otros formatos como
el binario. Una solicitud POST crea un nuevo recurso y una solicitud PUT actualiza un recurso.
El Servicio de pedidos, por ejemplo, tiene un punto final POST / orders para crear un pedido y
un punto final GET / orders / {orderId} para recuperar un pedido.

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.

El 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:

 Nivel 0: los clientes de un servicio de nivel 0 invocan el servicio realizando solicitudes


HTTP POST a su único punto final de URL. Cada solicitud especifica la acción a realizar,
el objetivo de la acción (por ejemplo, el objeto comercial) y cualquier parámetro.
 Nivel 1: un servicio de nivel 1 respalda la idea de los recursos. Para realizar una acción
en un recurso, un cliente realiza una solicitud POST que especifica la acción a realizar y
cualquier parámetro.
 Nivel 2: un servicio de nivel 2 utiliza verbos HTTP para realizar acciones: GET para
recuperar, POST para crear y PUT para actualizar. Los parámetros y el cuerpo de la
consulta de solicitud, si los hay, especifican los parámetros de las acciones. Esto
permite que los servicios utilicen la infraestructura web, como el almacenamiento en
caché de las solicitudes GET.
 Nivel 3: el diseño de un servicio de nivel 3 se basa en el principio terriblemente
llamado HATEOAS (Hipertexto como el motor del estado de aplicación). La idea básica
es que la representación de un recurso devuelto por una solicitud GET contiene
enlaces para realizar acciones en ese recurso. Por ejemplo, un cliente puede cancelar
un pedido utilizando un enlace en la representación devuelta por la solicitud GET que
recuperó el pedido. Los beneficios de HATEOAS incluyen ya no tener que conectar las
URL en el código del cliente (www.infoq.com/news/2009/04/hateoas-restful-api-
advantages).

Le recomiendo que revise las API REST en su organización para ver a qué nivel corresponden.

ESPECIFICANDO APIS DE REST


Como se mencionó anteriormente en la sección 3.1, debe definir sus API utilizando un lenguaje
de definición de interfaz (IDL). A diferencia de los protocolos de comunicación más antiguos
como CORBA y SOAP, REST originalmente no tenía un IDL. Afortunadamente, la comunidad de
desarrolladores ha redescubierto el valor de un IDL para API RESTful. El IDL REST más popular
es la especificación Open API (www.openapis.org), que evolucionó del proyecto de código
abierto Swagger. El proyecto Swagger es un conjunto de herramientas para desarrollar y
documentar API REST. Incluye herramientas que generan stubs de cliente y esqueletos de
servidor a partir de una definición de interfaz.

EL DESAFÍO DE OBTENER MÚLTIPLES RECURSOS EN UNA SOLA SOLICITUD

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.

EL DESAFÍO DE LAS OPERACIONES DE MAPEO PARA VERBOS HTTP

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.

BENEFICIOS E INCONVENIENTES DE REST

Existen numerosos beneficios al usar 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).

Supports Es directamente compatible con la comunicación de estilo de solicitud / respuesta.

 HTTP es, por supuesto, compatible con firewall.


 No requiere un intermediario, lo que simplifica la arquitectura del sistema.

Hay algunos inconvenientes para usar REST:

 Solo admite el estilo de comunicación de solicitud / respuesta.


 Disponibilidad reducida. Debido a que el cliente y el servicio se comunican
directamente sin un intermediario para almacenar los mensajes en el búfer, ambos
deben estar en ejecución durante el intercambio.
 Los clientes deben conocer las ubicaciones (URL) de las instancias del servicio. Como se
describe en la sección 3.2.4, este es un problema no trivial en una aplicación moderna.
Los clientes deben usar lo que se conoce como mecanismo de descubrimiento de
servicios para localizar instancias de servicio.
 Obtener múltiples recursos en una sola solicitud es un desafío.
 A veces es difícil asignar múltiples operaciones de actualización a verbos HTTP.

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.

gRPC es otra alternativa a REST. Echemos un vistazo a cómo funciona.

3.2.2 Usando gRPC

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.

Listado 3.1 Un extracto de la API de gRPC para el Servicio de pedidos

CreateOrderRequest y CreateOrderReply son mensajes escritos. Por ejemplo, el mensaje


CreateOrderRequest tiene un campo restaurantId de tipo int64. El valor de la etiqueta del
campo es 1.

gRPC tiene varios beneficios:

 Es sencillo diseñar una API que tenga un amplio conjunto de operaciones de


actualización.
 Tiene un mecanismo IPC eficiente y compacto, especialmente cuando se intercambian
grandes mensajes
 La transmisión bidireccional permite los estilos de comunicación RPI y de mensajería.
 Permite la interoperabilidad entre clientes y servicios escritos en una amplia gama de
idiomas.

gRPC también tiene varios inconvenientes:

 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.

3.2.3 Manejo de fallas parciales utilizando el patrón de disyuntor (Circuit breaker)

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.

Patrón: disyuntor (Circuit breaker)

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.

Una implementación ingenua(naive) de OrderServiceProxy se bloquearía indefinidamente,


esperando una respuesta. Eso no solo resultaría en una mala experiencia del usuario, sino que
en muchas aplicaciones consumiría un recurso valioso, como un hilo. Finalmente, la API
Gateway se quedaría sin recursos y sería incapaz de manejar las solicitudes. La API completa
no estaría disponible.

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.

Primero veremos cómo escribir proxies RPI robustos.

DESARROLLO DE PROXIMAS RPI ROBUSTA

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.

Limitar el número de solicitudes pendientes de un cliente a un servicio: imponga un límite


superior en el número de solicitudes pendientes que un cliente puede hacer a un servicio en
particular. Si se ha alcanzado el límite, probablemente no tenga sentido hacer solicitudes
adicionales, y esos intentos deberían fallar de inmediato.

Patrón de disyuntor: realice un seguimiento del número de solicitudes exitosas y fallidas, y si la


tasa de error excede algún umbral, dispare el disyuntor para que los intentos posteriores fallen
inmediatamente. Un gran número de solicitudes fallidas sugiere que el servicio no está
disponible y que enviar más solicitudes no tiene sentido.

Después de un período de tiempo de espera, el cliente debe intentarlo nuevamente y, si tiene


éxito, cerrar el disyuntor.

Netflix Hystrix (https://github.com/Netflix/Hystrix) es una biblioteca de código abierto que


implementa estos y otros patrones. Si está utilizando la JVM, definitivamente debería
considerar usar Hystrix cuando implemente los servidores proxy RPI. Y si está ejecutando en un
entorno que no es JVM, debe usar una biblioteca equivalente. Por ejemplo, la biblioteca Polly
es popular en la comunidad .NET (https://github.com/App-vNext/Polly).

RECUPERANDO DE UN SERVICIO NO DISPONIBLE

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

Servicio de entrega, y combina los resultados.

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.

3.2.4 Uso del descubrimiento de servicios

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.

Figura 3.4 Las instancias de servicio tienen direcciones IP asignadas dinámicamente.

DESCRIPCIÓN GENERAL DEL DESCUBRIMIENTO DEL 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.

El mecanismo de descubrimiento de servicios actualiza el registro de servicios cuando las


instancias de servicio comienzan y se detienen. Cuando un cliente invoca un servicio, el
mecanismo de descubrimiento del servicio consulta el registro del servicio para obtener una
lista de instancias de servicio disponibles y enruta la solicitud a una de ellas.

Hay dos formas principales de implementar el descubrimiento de servicios:

Los servicios y sus clientes interactúan directamente con el registro de servicios.

La infraestructura de implementación maneja el descubrimiento de servicios. (Hablo más


sobre eso en el capítulo 12.).

Veamos cada opción.

APLICANDO LOS PATRONES DE DESCUBRIMIENTO DEL SERVICIO DE NIVEL DE APLICACIÓN.


Una forma de implementar el descubrimiento de servicios es que los servicios de la aplicación
y sus clientes interactúen con el registro de servicios. La Figura 3.5 muestra cómo funciona
esto. Una instancia de servicio registra su ubicación de red con el registro de servicio. Un
cliente de servicio invoca un servicio al consultar primero el registro de servicio para obtener
una lista de instancias de servicio. Luego envía una solicitud a una de esas instancias.

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.

Este enfoque para el descubrimiento de servicios es una combinación de dos patrones. El


primer patrón es el patrón de auto registro. Una instancia de servicio invoca la API de registro
del registro de servicio para registrar su ubicación de red. También puede proporcionar una
URL de comprobación de estado, descrita con más detalle en el capítulo 11. La URL de
comprobación de estado es un punto final de API que el registro del servicio invoca
periódicamente para verificar que la instancia del servicio esté en buen estado y disponible
para manejar las solicitudes. Un registro de servicio puede requerir que una instancia de
servicio invoque periódicamente una API de “heartbeat” para evitar que caduque su registro.

Patrón: auto registro

Una instancia de servicio se registra con el registro de servicio. Ver


http://microservices.io/patterns/self-registration.html.

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.

Patrón: descubrimiento del lado del cliente

Un cliente de servicio recupera la lista de instancias de servicio disponibles del registro de


servicio y los equilibrios de carga entre ellas. Ver http://microservices.io/patterns/clientside-
discovery.html.

Netflix y Pivotal han popularizado el descubrimiento de servicios a nivel de aplicación. Netflix


desarrolló y abrió varios componentes: Eureka, un registro de servicios de alta disponibilidad,
el cliente Eureka Java y Ribbon, un sofisticado cliente HTTP que admite el cliente Eureka.
Pivotal desarrolló Spring Cloud, un marco basado en Spring que hace que sea muy fácil usar los
componentes de Netflix. Los servicios basados en Spring Cloud se registran automáticamente
con Eureka, y los clientes basados en Spring Cloud usan automáticamente Eureka para el
descubrimiento de servicios.

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 inconveniente del descubrimiento de servicios a nivel de aplicación es que necesita una


biblioteca de descubrimiento de servicios para cada idioma, y posiblemente marco, que utilice.
Spring Cloud solo ayuda a los desarrolladores de Spring. Si está utilizando algún otro marco de
Java o un lenguaje que no sea JVM como NodeJS o GoLang, debe encontrar otro marco de
descubrimiento de servicios. Otro inconveniente del descubrimiento de servicios a nivel de
aplicación es que usted es responsable de configurar y administrar el registro de servicios, lo
cual es una distracción. Como resultado, generalmente es mejor usar un mecanismo de
descubrimiento de servicios proporcionado por la infraestructura de implementación.

APLICANDO LOS PATRONES DE DESCUBRIMIENTO DE SERVICIOS PROPORCIONADOS POR LA


PLATAFORMA.

Más adelante, en el capítulo 12, aprenderá que muchas plataformas de implementación


modernas, como Docker y Kubernetes, tienen un registro de servicios incorporado y un
mecanismo de descubrimiento de servicios. La plataforma de implementación proporciona a
cada servicio un nombre DNS, una dirección IP virtual (VIP) y un nombre DNS que se resuelve
en la dirección VIP. Un cliente de servicio realiza una solicitud al nombre DNS / VIP, y la
plataforma de implementación enruta automáticamente la solicitud a una de las instancias de
servicio disponibles. Como resultado, el registro de servicios, el descubrimiento de servicios y
el enrutamiento de solicitudes son manejados completamente por la plataforma de
implementación.

La Figura 3.6 muestra cómo funciona esto.

La plataforma de implementación incluye un registro de servicio que rastrea las direcciones IP


de los servicios implementados. En este ejemplo, un cliente accede al Servicio de pedidos
utilizando el servicio de pedidos de nombres DNS, que se resuelve en la dirección IP virtual
10.1.3.4. La plataforma de despliegue equilibra automáticamente las solicitudes de carga en
las tres instancias del Servicio de pedidos.

Este enfoque es una combinación de dos patrones:

 Patrón de registro de terceros: en lugar de que un servicio se registre en el registro de


servicios, un tercero llamado registrador, que generalmente forma parte de la
plataforma de implementación, se encarga del registro.
 Patrón de descubrimiento del lado del servidor: en lugar de que un cliente consulte el
registro del servicio, realiza una solicitud a un nombre DNS, que se resuelve en un
enrutador de solicitud que consulta el registro del servicio y las solicitudes de
equilibrio de carga.

Figura 3.6 La plataforma es responsable del registro del servicio, el descubrimiento y el


enrutamiento de solicitudes. Las instancias de servicio son registradas en el registro de
servicio por el registrador. Cada servicio tiene una ubicación de red, un nombre DNS /
dirección IP virtual. Un cliente realiza una solicitud a la ubicación de red del servicio. El
enrutador consulta el registro de servicio y las solicitudes de equilibrio de carga en las
instancias de servicio disponibles.

Patrón: registro de terceros


Las instancias de servicio se registran automáticamente en el registro de servicio por un
tercero. Ver http://microservices.io/patterns/3rd-party-registration.html.

Patrón: descubrimiento del lado del servidor

Un cliente realiza una solicitud a un enrutador, que es responsable del descubrimiento del
servicio. Ver http://microservices.io/patterns/server-side-discovery.html.

El beneficio clave del descubrimiento de servicios proporcionado por la plataforma es que


todos los aspectos del descubrimiento de servicios son manejados completamente por la
plataforma de implementación. Ni los servicios ni los clientes contienen ningún código de
descubrimiento de servicios. En consecuencia, el mecanismo de descubrimiento de
servicios está fácilmente disponible para todos los servicios y clientes,
independientemente del idioma o marco en el que estén escritos.

Un inconveniente del descubrimiento de servicios proporcionados por la plataforma es


que solo admite el descubrimiento de servicios que se han implementado utilizando la
plataforma. Por ejemplo, como se mencionó anteriormente al describir el descubrimiento
a nivel de aplicación, el descubrimiento basado en Kubernetes solo funciona para servicios
que se ejecutan en Kubernetes. A pesar de esta limitación, recomiendo utilizar el
descubrimiento de servicios proporcionado por la plataforma siempre que sea posible.

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.

3.3 Comunicación utilizando el patrón de mensajería asincrónica

Cuando se usan mensajes, los servicios se comunican intercambiando mensajes de forma


asíncrona. Una aplicación basada en mensajería generalmente usa un intermediario de
mensajes, que actúa como intermediario entre los servicios, aunque otra opción es utilizar
una arquitectura sin intermediario, donde los servicios se comunican directamente entre
sí. Un cliente de servicio realiza una solicitud a un servicio enviándole un mensaje. Si se
espera que la instancia del servicio responda, lo hará enviando un mensaje por separado al
cliente. Como la comunicación es asíncrona, el cliente no bloquea la espera de una
respuesta. En cambio, el cliente está escrito asumiendo que la respuesta no se recibirá de
inmediato.

Patrón: mensajería

Un cliente invoca un servicio mediante mensajería asincrónica. Consulte http: //


microservices .io / patterns / communication-style / messaging.html.

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.

También podría gustarte