sábado, 23 de mayo de 2020

Capítulo 2: Usando una Api Gateway - Microservices Designing Deployment



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.

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.