Microservicios Java fácil y rápido con Micronaut

10 octubre 2018

Microservicios Java fácil y rápido con Micronaut

by Iñaki Reta

Te contamos más sobre el framework Micronaut para la implementación y el testeo de microservicios de forma rápida y sencilla.

Microservicios Java fácil y rápido con Micronaut

  •  Micronaut
    • IoC
    • Autoconfiguración
    • Orientado a microservicios
      • Configuración
      • Discovery server
      • Client side load balancing
      • Distributed Tracing
      • Circuit breaker
    • Ligero y rápido
    • Conclusión

Micronaut

Micronaut es un framework desarrollado con la idea de poder implementar y testear microservicios de una forma muy sencilla y rápida.

Nace con la idea de simplificar aún más el desarrollo de microservicios y también corregir algunas deficiencias que tiene Spring como framework más generalista. Ha sido desarrollado por los creadores de Grails y por algunos desarrolladores de Pivotal, que han intervenido en Spring. Ha sido diseñado para tener en cuenta entornos donde la memoria es un factor limitante, donde es necesario que arranquen sumamente rápido, donde se use lo mínimo reflexión, que no se deban usar muchos proxies y que sean fácilmente testeables.

Aunque todavía es un framework bastante joven, la documentación que están desarrollando es muy interesante y completa. Es el sitio de referencia donde se pueden encontrar respuesta a todas las dudas que podamos tener.

IoC

Micronaut define un contenedor de inversión de control, en el que se tendrán disponibles los distintos beans que se definan. Para ello implementan la especificacón propuesta por Java en la JSR-330 y definen distintos scopes para los beans.

  • @Sngleton: indica que únicamente existirá una instancia.
  • @Context: este bean deberá crearse al mismo tiempo que le ApplicationContext correspondiente.
  • @Prototype: se creará una nueva instancia del bean cada vez qeu sea necesario inyectarlo para resolver una dependencia.
  • @Infraestructure: indica que este bean no podrá ser reemplazado.
  • @ThreadLocal: se asocia el bean por thread a través de un ThreadLocal.
  • @Refreshable: permite que el estado de bean pueda ser refrescado a través del end-point /refresh

También define un mecanismo para seleccionar un bean concreto cuando tenemos disponibles varias instancias utilizando el par @Qualifier y @Named para sugerir al motor de IoC que inyecte la instancia que nos interese a nosotros.

Para resolver la inversión de dependencias, adopta un camino distinto al de Spring. Mientras Spring define una serie de proxies en memoria que resuelven el problema en tiempo de ejecución, Micronaut lo resuelve en  tiempo de compilación. Esta aproximación, hace que en tiempo de ejecución no se invierta nada de tiempo en construir al vuelo objetos y comportamientos. Únicamente se hace uso de las clases existentes. Paralelamente, también ofrece un mecanismo para acceder directamente al contenedor de beans y recuperar el que nos interese en este momento, pero esta segunda opción ya estaría resolviendo dicha dependencia en tiempo de ejecución y sería algo más lenta.

 

Autoconfiguración

En este punto, Micronaut se ha inspirado muy fuertemente en Spring hasta el punto que asume una colección importante de properties que ya tienen un valor por defecto. Con esto consigue proporcionar una configuración del microservicio bastante estable y robusta que cubre el 95% de los casos que puedan darse, pero dejando una puerta abierta para que podamos modificar y configurar a nuestra elección, en caso de que así lo necesitemos.

Además de la redefinición de propiedades de nuestro entorno, Micronaut define un mecanismo muy sencillo para sobreescribir beans enteros. Define una anotación @Replace que hace justo esto: reemplazar el bean indicado por el bean que se esté definiendo en la clase que anota.

 

Orientado a microservicios

Uno de los valores que viene preconfigurado es el puerto en el que escuchará nuestro microservicio, y curiosamente lo define de forma aleatoria. Este comportamiento tiene todo el sentido del mundo dado que asume que existirá un Service Discovery disponible. Lo interesante en este caso es que nuestro microservicio se presente al discovery en el arranque y que sea este servicio el encargado de indicar a los clientes donde está físicamente la instancia.

 

Configuración

También ofrece mecanismos muy sencillos para interactuar con distintos componentes típicos de una arquitectura orientada a microservicios. Con una anotación @Require se puede definir que valores de configuración puede utilizar en función del entorno que se desee. En este caso, los valores que utilizará serán configuraciones que tendrá accesibles de forma local. En el caso de que esta configuración esté custodiada por Consul, únicamente tendremos que indicar en nuestro fichero de configuración local la dirección y el puerto donde está disponible dicho servidor Consul.

 

Discovery server

Como comentábamos en el párrafo anterior, podemos configurar nuestro microservicio de Micronaut para que se registre en algún Service Discovery como pueden ser Eureka, Consul o ZooKeeper. La configuración se reduce a definir unas properties indicando la url donde esté disponible el discovery, por ejemplo:

eureka:
client:
registration:
enabled: true
defaultZone: "${EUREKA_HOST:localhost}:${EUREKA_PORT:8761}"

o si por el contrario fuera contra un Consul sería:

consul:
client:
registration:
enabled: true
defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"

Client side load balancing

Micronaut por defecto implementa una política de Round-Robin en el lado cliente para balancear la carga entre las distintas instancias que estén registradas en el Service discovery. Este balanceo se hace utilizando Netflix Ribbon de tal forma que Micronaut únicamente necesita conocer un par de propiedades para poder definir cada cuanto tiempo tiene que comprobar la lista de microservicios disponibles y ver a cual encaminar las peticiones.

 

Distributed Tracing

Cuando decidimos implementar una arquitectura basada en microservicios, un problema muy interesante es el de cómo se trazan las peticiones a lo largo de toda nuestra granja de microservicios. Para ello, es buena idea aplicar las soluciones propuestas respecto a Distributed tracing. Y otra vez, con Micronaut podemos integrar de forma muy sencilla tanto Zipkin como Jaeger. Todo se reduce a utilizar las anotaciones que nos ofrece el framework:

  • @NewSpan: creará un nuevo span envolviendo al método de llamada o al tipo reactivo.
  • @ContinueSpan: continuará con un span existente.
  • @SpanTag: esta anotación se usa en los argumentos de los métodos, de tal forma que se incluya el valor de dicho argumento dentro de la etiqueta span.

La forma de elegir entre Zipkin o Jaeger vuelve a ser similar a como elegíamos los discovery servers: a través de una property de configuración y añadir la dependencia correspondiente en nuestro pom.xml o gradle.properties.

Circuit breaker

Nuevamente, Micronaut da una solución para integrar otro de los patrones que se suelen definir en una arquitectura de microservicios. En este caso, la forma que tiene de facilitar el patrón Circuit Breaker es nuevamente definiendo una serie de anotaciones.

De primeras ofrece opciones para reintentar las peticiones una serie de veces o cada x tiempo antes de fallar directamente. Además, ofrece la posibilidad de integrar Netflix-Hystrix para dar un comportamiento controlado cuando se decida “abrir el circuito”.

 

Ligero y rápido

Han puesto especial hincapié en que los microservicios sean muy ligeros y desplieguen de una forma muy rápida. Para ello, utilizan como contenedor de servlets Nett que utiliza las librerías de entrada/salida de java.nio, convirtiendo en no bloqueante toda interacción de entrada y salida.

Ya hemos comentado previamente que para hacer más rápida la ejecución, la IoC se realiza en tiempo de compilación, en vez de generar proxies. Este detalle, hace que los tiempos de ejecución se reduzcan drásticamente porque no hay que inferir ningún comportamiento en tiempo de ejecución. Este comportamiento está “pre-anclado” por la simple compilación de las clases.

Por último, también añaden un componente lazy. Por defecto, todos los beans se crean de forma retardada, es decir, cuando se van a usar por primera vez es cuando se completan todas las dependencias asociadas. Esto hace que los despliegues también se vean favorecidos por este hecho y los tiempos se reduzcan al mínimo.

Conclusión

Micronaut es un framework muy joven pero que promete bastante. Al centrarse únicamente en ofrecer facilidades para desarrollo de microservicios, consigue simplificar muchos aspectos que en otros frameworks suele resultar más farrragoso.

En siguientes posts explicaremos como se puede convertir un microservicio implementado con Spring en uno implementado con Micronaut, de tal forma que podremos comparar las bondades y las complejidades que ofrece Micronaut para el desarrollo.

Volver a la listaSiguiente artículo
arrow

Titulo

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.