Cómo nace la necesidad de ocupar microservicios
Detrás de toda solución hay un problema que queremos solucionar. Voy a partir explicando una necesidad para que luego podamos entender mejor la definición de microservicios: Hoy en día necesitamos que las aplicaciones puedan escalar fácilmente a cada vez un número más grande de usuarios y ser fáciles de dar mantenimiento (con casi nulas ventanas de mantenimiento). Además está el tema de tratar de desarrollar en el menor tiempo posible y con la mejor calidad posible.
- Dividir una aplicación en componentes siempre ha sido una necesidad en el desarrollo de sistemas. Esto nos permite reducir la complejidad del todo en partes más sencillas de desarrollar y probar.
- Escalabilidad de las aplicaciones: Hoy muchas aplicaciones tienen el reto de llegar a más usuarios, realizar procesos complejos, tener tiempos de respuesta cada vez menores, tener disponibilidad (Uptime) un 99.99% del tiempo, entre otras.
- Desarrollos más rápidos: Es decir, la construcción de aplicaciones sobre una arquitectura que me permita simplificar el desarrollo y entregar productos de mejor calidad (es decir, ser mejor desarrolados, mejor probados y además ser ágiles en el desarrollo para dar mejor apoyo a las áreas de negocio de nuestra organización).
- Optimizar el uso de recursos: Nuestras máquinas tienen recursos limitados y debemos ver la forma de acomodar la mayor cantidad de aplicaciones posibles con el mejor rendimiento posible. Es un balance entre el límite de los recursos de los que disponemos contra la cantidad de las aplicaciones desplegadas y usadas (Si vemos las nuevas tendencias, con el uso de contenedores de aplicaciones, veremos justamente esto: Un mejor uso de nuestros recursos).
Hasta allí nada suena nuevo. Esas son necesidades de nuestro día a día. Pero, con esas ideas en mente: ¿Podemos construir una aplicación financiera que tenga un servicio para alta de contratos de crédito y que internamente ese servicio haga todo? Es decir crea una persona, crea una solicitud, crea un contrato y aplica las operaciones contables para la disposición del dinero? Toma en cuenta que debemos probar cada una de las funcionalidades, y al tener integradas (acopladas) varias tareas de distinta índole (no es lo mismo crear una persona que crear una solicitud) generará un gran abanico de casos de prueba. Esta estrategia no parece la mejor estrategia. Eso, mis amigos, es el enfoque monolítico. No sólo eso, por la forma de construir este tipo de aplicaciones tenemos un WAR o EAR que seguro ocupará 50MB o 100MB de espacio en disco (excesivo si sólo quería crear un servicio web). Además un “servicio gigantesco” es más lento comparado con un “servicio pequeño”: Es más rápido hacer una tarea puntual (crear persona = 1 insert) versus hacer todo en una sóla llamada a un servicio (crear persona + crear solicitud + crear contrato + aplicar operaciones contables = varios inserts y updates). Podemos escalar horizontalmente la aplicación monolítica con un buen balanceador de carga siempre y cuando tuvimos cuidado de diseñar la aplicación pensando en que podía ejecutarse en múltiples instancias a la vez, pero aún cuando esto pudiera ser posible, cualquier cambio en uno de los componentes provocará el redespliegue de toda la aplicación. En un enfoque monolítico, si debo actualizar algo, dejaré fuera de línea TODA mi aplicación. Si no sólo tengo aquí el alta de contratos, sino también otros servicios como impresión de cheques, análisis crediticio de los clientes, entre otros, lo que estoy haciendo es dejar completamente fuera de línea TODA mi operación. En estos tiempos eso debería ser impensable.
Quizás en una aplicación pequeña esta estrategia no tiene muchos problemas, pero: ¿Qué pasa si la aplicación es grande, tenemos partes que hacen diferentes tareas y debo hacer cambios en la aplicación? Si nuestra aplicación es un “core” bancario y vamos a tener componentes para la administración de personas, de préstamos y de cobranza va a haber un gran impacto cuando se aplique un cambio o mejora. Si yo modifiqué el componente de personas porque se varió una regla para la creación de personas ¿Suena lógico redesplegar TODA la aplicación afectando también a préstamos y a cobranza? Aún cuando no cambié los beans para la administración de las personas ni cambié las APIs de los servicios de personas, tuve que redesplegar todo. Esto porque todo está empaquetado en un solo WAR o EAR y esto significa que todo quedará fuera de línea durante la actualización: En un enfoque monolítico varios servicios estarán fuera de línea sólo por actualizar una pequeña parte de toda la aplicación.
Eso no suena lógico: Es como si mi vecino (personas) fuera a remodelar el interior de su casa y a mí (crédito) me pidieran salir de mi casa porque van a actualizar “toda la cuadra” debido a las remodelaciones del vecino.
Además, una aplicación monolítica y grande tiende a borrar los límites entre los diferentes componentes. Siendo exagerados quizás un día podrías ya no ver una separación clara entre la generación de personas y contratos después de meses o años de mantenimiento a las aplicaciones, esto porque todo está en la misma aplicación y el desarrollador que da mantenimiento puede tomarse libertades por desconocimiento o descuido.
Estos problemas han conducido buscar un nuevo enfoque, el estilo arquitectónico de microservicios: La creación de aplicaciones como un conjuntos de servicios simples. Cada servicio es independiente tanto en su desarrollo como en su despliegue. Los servicios deberían tener alta cohesión y bajo (o nulo) acoplamiento. Y deben poder ser administrados y desarrollados por diferentes equipos.
Si pensamos en microservicios imaginemos que cada servicio es como una pieza o bloque. Así puedo tomar el bloque “servicio de personas” y conectarlo con el bloque “servicio de contratos”. Y el bloque “servicio de personas” también puede conectarse con el bloque “servicio de préstamos”. Si actualizamos “contratos” no tenemos porque dar de baja “personas” o “préstamos”. Todo sigue operando salvo “contratos”, hasta que termine su actualización.
Si pensamos en piezas, es mas sencillo aprovechar las cajas (servidores/contenedores) si divido mi aplicación en piezas pequeñas: Estas pueden encajar con mayor dinamismo en contenedores de tamaños variables.
¿Qué es un microservicio?
Ventajas de las aplicaciones orientadas a microservicios
¿Hay desventajas?
Si pensamos en que tendremos muchos servicios pequeños e independientes pronto nos daremos cuenta que hay más elementos que administrar comparado contra un WAR o EAR monolítico. Además pueden haber llamadas entre componentes independientes y diferentes versiones por administrar de un mismo componente, por lo que la tarea de administración de un sistema basado en microservicios puede volverse un dolor de cabeza.
Y si pensamos en microservicios como servicios web entonces además tenemos la desventaja del tráfico que se producirá en la red. El rendimiento también se ve afectado: Cuantas más llamadas por red haya, más lento el tiempo de respuesta.
En la siguiente sección les cuento como podemos atender estas aparentes desventajas para que no sean desventajas.
¿Desventajas? No realmente.
Puedo mencionarles algunos lineamientos que yo he llevado a la práctica en sistemas de alta disponibilidad y con posibilidad de escalamiento en el mundo Java (1000, 2000 peticiones por segundo, no hay límite si unimos la idea de los microservicios al uso de múktiples contenedores de aplicaciones):
- Se deben tener lineamientos claros de desarrollo: Debe existir una única forma de estructurar los proyectos, se deben identificar componentes reusables y que todos deben usar de forma común (no rehacer funbcionalidad que ya existe en un componente genérico). Y también importante sólo debe permitirse el uso de frameworks o librerías “autorizadas” por un comité o equipo especializado. Esto es la arquitectura para el desarrollo de un sistema.
- Se debe usar un administrador de versiones del lado del desarrollo y que facilite el despliegue de aplicaciones: La combinación de Apache Maven y un contenedor OSGi como Apache Karaf son bastante útiles para gestionar los componentes en tiempo de desarrollo y despliegue. En otros posts mencionaré soluciones que se apoyan en estas herramientas como son JBoss Fuse (producto comercial de Red Hat) y Fabric8 (Opensource).
- Si hablas de servicios no pienses sólo en servicios web: Por definición un servicio es la acción y efecto de servir (la definición no especifica tecnologías). Si pensamos en servicios seguramente pensaremos en SOA pero no en todos los casos se requiere comunicación por red: ¿Qué pasa si los servicios independientes que queremos llamar se encuentran ya en la misma Java Virtual Machine (JVM) donde se ejecuta nuestro programa? Esto es OSGi: Es “como usar SOA” pero aplicado dentro de la JVM. En lugar de invocar servicios por http mejor llamar a clases y métodos en mis dependencias. Haré un post al respecto más adelante.
Definición de Fowler:
http://martinfowler.com/articles/microservices.html