Vtech Awakens - Microservicios

El Viernes tecnológico (a.k.a vtech) es un evento en el cual amantes de la ciencia, y en especial de la informática, nos reunimos para aprender de la experiencia de otros, escuchando presentaciones y por supuesto aprovechando para celebrar la amistad que nos une.

Este es un evento que solíamos hacer con frecuencia en años anteriores y por circunstancias de la vida lo tuvimos que poner en pausa. En este año 2016 queremos volver a despertar la fuerza que nos trae esta sana actividad (de ahí el título del post) y empezar a realizarla de forma mensual.

La primera convocatoria se realizó en Sodep el pasado viernes 18 de febrero, donde me tocó a mi hablar sobre una arquitectura que usamos en un proyecto realizado en Sodep, la arquitectura de microservicios.

El siguiente blog post se expande sobre lo expuesto en mencionada ocasión.

¿Qué es un Microservicio?

Uno de los primeros propulsores de la Arquitectura de Microservicios, Martin Fowler, explica en su sitio que un Microservicio es un componente software que debe cumplir la mayoría (no todos) de los siguientes puntos:

  • Expone algún servicio
  • Corre sus propios procesos
  • Construido entorno a capacidades del negocio
  • Deployable de forma independiente
  • Mínimo de gestión centralizada

Cuando decimos que el componente debe estar construido entorno a capacidades del negocio, queremos decir que el componente debe cubrir y solucionar una parte bien concreta y específica de un negocio. Por ejemplo, para un sitio de ventas online como Amazon, podría haber un microservicio que se encargue de proveer el catálogo de ítems que están en venta, y podría también existir otro que se encargue de forma exclusiva de las funcionalidades de shipping.

Una arquitectura de microservicios es entonces una suite de componentes con las características citadas arriba.

Monolítico vs Microservicio

Para entender qué es un Microservicio muchas veces es útil compararlo con la arquitectura más tradicional y con la que todos estamos más familiarizados, la Monolítica.

En una arquitectura monolítica uno escala una aplicación, replicando todo el sistema y creando copias idénticas del mismo. Este enfoque se considera poco flexible y lo que permite una arquitectura de microservicios es escalar de forma individual cada uno de los componentes, y de esa forma evolucionar cada uno de forma separada a los demás, de la forma más rápida posible y afectando lo menos posible a los otros componentes.

Figura 1 (Fuente: http://martinfowler.com/articles/microservices.html)

Empresas que migraron a una arquitectura de microservicios (o que arrancaron con una) hacen mucho énfasis en el rapid deployment, es decir, que el proceso de deployar y poner en producción una funcionalidad sea lo más rápido posible, y con el menor impacto a las demás funcionalidades. Esta es una de las claves de esta arquitectura.

Se presentan a continuación dos esquemas a modo de ejemplo. En el primero (Figura 2) se tiene una aplicación monolítica que accede a una base de datos para obtener información.

Figura 2 – Ejemplo monolítico

En el segundo ejemplo (Figura 3), cada microservicio tiene su propio almacenamiento de datos. También podemos notar que existe un API Gateway en el medio, que representa el API común al cuál acceden los clientes (browsers, teléfonos, etc.) y que se encarga de hacer el routing de las peticiones a los distintos microservicios.

Figura 3 – Ejemplo microservicios

Caso de Uso en Sodep

El caso de uso en Sodep donde implementamos una arquitectura de microservicios es el de un cliente cuyo software expone un API RESTFul. Denominaremos para mejor entendimiento a este API como “MY API”. Este software necesita comunicarse con API de terceros para obtener información y presentar la misma al usuario final.

El software del cliente solicita a los terceros que implementen “MY API” para poder llevar a cabo la comunicación. Por supuesto que la realidad marca que la mayoría de estos terceros, ya exponen su propia API, ya sea a través de REST o SOAP, y el trabajo de “hablar el idioma” de estos terceros debería quedar a cargo del software del cliente.

Solución propuesta

Dada esta circunstancia, la solución que se propuso fue implementar varios microservicios traductores capaces de hablar SOAP/REST con terceros, y traducir la información de estos a información que se adecue al API “MY API” expuesto por el software del cliente.

Figura 4 – Microservicios traductores

La idea inicial entonces fue deployar de forma separada cada microservicio, cada uno con su propio server, utilizando la el framework Spring Boot. Lo que permite este framework es escribir de forma ágil y con mínimo de configuración, aplicaciones web que exponen algún API REST y que estas aplicaciones puedan ser deployadas con un Tomcat embedido.

Se implementarían entonces dos principales módulos:

  • Dispatcher: la aplicación WEB que implementa MY API y que haría el routing de las peticiones desde los clientes a cada uno de los microservicios traductores
  • Microservicios traductores: aplicaciones WEB que también implementan MY API y que saben comunicarse con software de terceros

Un ejemplo simplificado de cómo se implementaría un microservicio para este caso de uso, sería como el siguiente bloque de código Java

@RestController 
public class MyServiceController {  
    SoapClient soapClient;
    @RequestMapping("/myapi/number")
    public String getNumber() {
       Integer num = soapClient.getNumber();
        return num.toString();
    }
}

Nótese como el microservicio del ejemplo espera peticiones en la ruta “/myapi/number”, cuando recibe una petición en esa ruta, se comunica por SOAP con el tercero, y luego traduce la respuesta a algo esperado por la especificación de MY API.

Un deploy/test de un microservicio como el anterior sería tan sencillo como seguir los siguientes pasos

> mvn package
> java -jar target/ms01.jar
> curl http://localhost:8081/myapi/number
12345

Donde el segundo paso estaría levantando un Tomcat embedido gracias a la forma en cómo configuramos nuestra aplicación Spring Boot.

De forma similar al ejemplo anterior, el Dispatcher se implementaría de la siguiente manera.

@RestController 
public class DispatcherController {  
    @RequestMapping("/myapi/{ms}/number")
    public String getNumber(String ms) {
        Client msClient = getMicroService(ms);
        String num = msClient.getNumber();
        return num;
    }
}

Y se deployaría como sigue

> mvn package
> java -jar target/dispatcher.jar
> curl http://localhost:8080/myapi/ms01/number
12345  

Analizando la solución…

La solución descrita anteriormente fue sometida a un análisis, donde para probar su factibilidad se levantaron varios microservicios en un servidor, a modo de hacer una prueba de stress.

Los números arrojados por estas pruebas en cuanto a consumición de recursos, no fueron del todo alentadores. Tampoco resultó alentador el monitoreo y las actividades de mantenimiento que implicaría cada microservicio por separado. Debido a estas razones se optó finalmente por simplificar la solución propuesta e ir por una arquitectura más monolítica.

Solución final

Se terminó implementando una arquitectura de plugins, es decir, aplicaciones Java empaquetados en un JAR que serían levantadas de forma dinámica en el Dispatcher. Estos plugins serían los que finalmente se comunicarían con los software de terceros e implementarían la traducción, es decir, serían los microservicios.

¿Es esta solución una arquitectura de microservicios?

Pues si vemos los criterios que debe cumplir un componente software para ser considerado un microservicio y que hemos citado arriba, la arquitectura propuesta no puede considerarse como una arquitectura 100% pura de microservicios. Sobre todo por el hecho de cada cada microservicio no corre sus propios procesos, es decir, cada JAR de un plugin se deploya en el mismo servidor que el Dispatcher, lo cual de alguna manera ata a cada plugin con el resto de la aplicación.

A pesar de esto, hay que mencionar que con la solución final sí pudimos lograr la ventaja de que cada plugin es deployable de forma separada a los demás, es decir, no hace falta deployar todo el Dispatcher más los otros plugins, con el objetivo de levantar un solo plugin.

Empresas que usan una arquitectura de Microservicios

Netflix

Una de las primeras grandes empresas en implementar esta arquitectura fue Netflix. Ellos migraron de un ambiente monolítico(un WAR de 2GB) a uno de microservicios en el año 2008, debido a que su proceso de deployment era considerado muy lento, y no les permitía escalar su sistema a la rapidez que exigía el mercado y la competencia.

También cuenta una anécdota que un “;” faltante al parsear los reviews de películas, hizo que caiga Netflix por varias horas en el año 2008. Identificar y solucionar este problema les tomó mucho tiempo, siendo el Fault Isolation otros de los motivos por el cual decidieron migrar su arquitectura.

Hoy en día Netflix tiene más de 500 microservicios, lo que entre otras cosas les permite soportar más de 50 millones de subscriptores y aproximadamente 2 billones de API Requests por día. ¡WOW!

Spotify

Spotify se inspiró en Netflix y decidió desde el vamos optar por esta arquitectura. Ellos separan sus microservicios por team de desarrollo, encargándose cada team de un microservicio en particular, y que este microservicio haga una tarea bien específica y de la mejor manera posible.

Cada team tiene su propio stack de desarrollo, su propio UX designer, front en developer, etc. El único lenguaje permitido en producción es Java, no tenían esta restricción al comienzo, pero a medida que fueron evolucionando se dieron cuenta de que la mayoría de los microservicios que se iban implementando y que funcionaban bien, estaban escritos en Java, lo cual les llevó a tomar la determinación de poner como requisito el uso de este lenguaje.

Además ellos realizan sus code review cross microservicios, es decir, los team se evalúan el código entre ellos, y el hecho de que este código esté escrito en el mismo lenguaje facilita mucho esa evaluación.

De forma similar a Netflix, Spotify hoy en día cuenta con más de 800 microservicios, soportando una carga muy alta y una evolución muy rápida gracias a la arquitectura que cuenta de microservicios.

Conclusión

Como conclusión, cuando se tiene que tomar la decisión de optar por una arquitectura de microservicios, se tiene que analizar si los siguientes puntos son considerados como críticos:

  • Escalar rápidamente. Delivery continuo y rapid deployment
  • Resistencia a fallos/fault isolation (ejemplo; el “;” de Netflix)
  • Availability. ¿Un microservicio abajo? No hay problema, levantamos otro

Por otro lado, también es muy importante, antes de optar por esta arquitectura, asegurarse de estar a una buena altura tecnológica en cuanto a:

  • Capacidad de monitoreo básica
  • Provisión rápida de servidores
  • Mecanismo o framework que permita un deploy rápido de aplicaciones (ejemplo Spring Boot)

Referencias