Imagina que se está
desarrollando la típica API REST en una arquitectura monolítica a la cual se
puede acceder desde el dominio:
api.myapi.com
Ahora imagino que la api
tiene un servicio disponible para consultar un producto:
GET /products/{productId}
Ahora imaginemos que,
por las necesidades de la aplicación y con el propósito del rendimiento, se
requiere obtener no sólo la información del producto si no más información en
una única llamada. Por ejemplo, se podría requerir la cantidad de comentarios que
ha tenido ese producto, la cantidad de valoraciones y la media de esta,
preguntas y respuestas así como productos relacionados con ese producto.
En este caso, se podría
considerar tener un servicio como:
GET
/products-details/{productId}
Como aplicación monolítica, puede existir un balanceador de carga el cuál redirige la petición a una de las distintas instancias que haya detrás. La instancia procesa la petición, consulta todos los datos en la base de datos y devuelve la respuesta al cliente.
Como aplicación monolítica, puede existir un balanceador de carga el cuál redirige la petición a una de las distintas instancias que haya detrás. La instancia procesa la petición, consulta todos los datos en la base de datos y devuelve la respuesta al cliente.
La arquitectura de
microservicios tiene un planteamiento distinto desde que cada microservicio
contiene su propia información. Se podrían plantear los siguientes
microservicios para la misma aplicación:
- Product Service: microservicio del producto.
- Comments Service: microservicio de comentarios.
- Rating Service: las valoraciones de un producto.
- Answer and question Service: preguntas y respuestas.
Cada uno de estos
microservicios estará desplegado en su propio subdominio como puede ser:
comments.api.myapi.com
En este momento, surge
la duda de cómo se tendrían que conectar las distintas aplicaciones que hacen
uso de estos microservicios.
Comunicación directa del
cliente al microservicio
En principio, cualquier
aplicación se podría conectar a cualquier microservicio accediendo a su url.
Así que, en el caso de solicitar la información anterior debería hacer una
petición a cada uno de los microservicios que se han descrito anteriormente.
Esto tiene varios retos.
Uno de ellos es que la fina granularidad de los servicios expuestos se adapte a
las necesidades de los distintos clientes. En el ejemplo anterior la aplicación
tiene que hacer 4 llamadas separadas. Esto podría dar lugar a muchas más
llamadas en aplicaciones más complejas llegando a no ser eficiente. Otro
problema es que la lógica para obtener toda la información se traslada a las
aplicaciones.
Otro problema es que
hace difícil refactorizar los microservicios. Es posible que, con el tiempo, se
necesite dividir un microservicio en dos o fusionar dos microservicios en uno.
Sin embargo, si los clientes se comunican directamente con estos
microservicios, puede llegar a ser extremadamente difícil realizar este tipo de
acciones.
Por estas razones
raramente tiene sentido para los clientes hablar directamente con los
microservicios.
Usando API Gateway
Una estrategia es usar
lo que se conoce como API Gateway. El propósito de esta es encapsular la
arquitectura interna y proveer de una API específica para cada cliente. Una API
Gateway provee de un único punto de entrada al sistema. Además, esta podría
tener otras responsabilidades como la autenticación, el balanceo de carga, la
caché, etc.
La API Gateway tiene la
responsabilidad de resolver las peticiones que lleguen invocando a
distintos microservicios y componiendo la respuesta a partir de las distintas
respuestas de cada microservicio.
Un API Gateway puede
proveer a cada cliente con su propia API personalizada. Por ejemplo, se podría
crear una API personalizada para clientes móviles.
Un gran ejemplo de API
Gateway es Netflix API Gateway. El servicio de Netflix está disponible para
cientos de tipos de dispositivos diferentes, desde un móvil a una televisión,
etc. En un principio trataron de crear una única API que cubriera las
necesidades aunque no tardaron en descubrir que no funcionaría debido a las
necesidades específicas de cada dispositivo.
A día de hoy, ellos usan
una API Gateway la cuál provee un API específica por dispositivo.
Beneficios y desventajas
de la API Gateway
El principal beneficio
de utilizar una API Gateway es encapsular la estructura interna de la
aplicación. En vez de invocar a los servicios específicos, el cliente
simplemente habla con la API Gateway. Esta API Gateway provee para cada cliente
un API específica reduciendo en número de peticiones entre los clientes y la
aplicación a la par que simplifica el código del cliente.
El principal problema es
que se añade otro componente al esquema el cual debe ser desarrollado,
desplegado y mantenido.También se corre el riesgo de que este componente se
convierta en el cuello de botella del desarrollo.
A pesar de estos inconvenientes
tiene sentido usar una API Gateway en la mayoría de las aplicaciones del mundo
real.
Implementando una API
Gateway
Rendimiento y
escalabilidad
No hay muchas compañías
que operan al nivel de Netflix pero para la mayoría de las aplicaciones el
rendimiento y la escalabilidad es un factor fundamental. Para ello se deberá
analizar las tecnologías que mejor cubran las necesidades del proyecto.
Usando un modelo de
programación reactivo
Una de las formas más
simples en las que una API Gateway puede procesar un petición es redirigir esta
a un microservicio, que el microservicio devuelva una respuesta y que el API
Gateway devuelva esta respuesta al cliente.
Una forma un poco más
compleja sería que la petición requiera de consultar varios microservicios
independientes entre sí. Esto quiere decir que la API Gateway puede hacer
llamadas a los distintos microservicios de manera concurrente para
posteriormente componer la respuesta para el cliente.
La complejidad aumenta
cuando hay una dependencia entre las distintas llamadas. Por ejemplo, en el
caso de Amazon los usuarios podrían tener un lista de deseos que consultar en
primera instancia y posteriormente obtener la información de cada producto. Una
vez obtenida la información esta se compone para dar una respuesta al
cliente.
Una forma usual de
componer la respuesta es utilizar una estrategia de callbacks asíncronos. Esta
estrategia, en casos complejos, suele llevar a popular “callback hell” donde el
código será difícil de comprender y propenso a errores.
Una estrategia para
evitar este problema es utilizar un estilo declarativo usando una estragegia
reactiva. Ejemplos de abstracciones reactivas podrían ser Promise en
JavaScript, Future en Scala, etc.
Invocación de servicios
Una aplicación basada en
microservicios tiene una naturaleza de sistema distribuido y, por lo tanto,
debe usar un mecanismo de comunicación entre los procesos o “Inter Process
Communications” (IPC). Hay dos tipos de tipos de IPC, síncronos y asíncronos.
Los mecanismos síncronos
están basados en HTTP o Thrift. Un ejemplo típico sería una API Rest a través
del protocolo HTTP.
Los mecanismos síncronos
son mecanismos basados en mensajes. Algunas de las implementaciones podrían ser
RabbitMq, Kafka, etc.
Service Discovery
La API Gateway necesita
conocer la localización (IP y el puerto) de los distintos microservicios con
los cuales se comunica. Tradicionalmente se añadían estas localizaciones de
forma no dinámica pero en aplicaciones basadas en la nube, añadir estas
localizaciones no es una tarea trivial.
En la nube, el número de
instancias cambian dinámicamente dependiendo de las necesidades del sistema
(autoscaling). Como consecuencia, la API Gateway necesita un mecanismo para
descubrir la localización de los servicios. Hay dos estrategias, que se
describirán más en profundidad, server-side discovery y cliente-side discovery.
Tratando los fallos
parciales
Estos problemas surgen
en todos los sistemas distribuidos cuando un servicio hace una llamada a otro y
este no está disponible. La API Gateway debería poder trata estos con estos
fallos dependiendo de cada caso concreto. Por ejemplo, si el servicio de
comentarios no está disponible en ese momento la API Gateway debería poder
responder con los detalles del producto, los productos recomendados, etc.
También debería
contemplar devolver datos cacheado en los casos en los que estos estén
disponibles. Esto significa que, en el caso de fallar un servicio se podrían
utilizar estos datos de manera que el impacto en el cliente sea mínimo.
Resumen
Para la mayoría de las
aplicaciones va a ser de gran utilidad tener un API Gateway como único punto de
entrada al sistema. Esta API Gateway es responsable del enrutado de las
peticiones, la composición de las distintas respuestas y traducción de
protocolos en el caso de ser necesario. Este provee de un API específico para
cada aplicación cliente.
El API Gateway debe ser
capaz de tratar los posibles errores de disponibilidad de manera que sea capaz
de devolver parte de la información o cubrir esa falta de información con
información cacheada o por defecto.