Lo siguiente es solo una traduccion al trabajo del señor Alexander Lukyanchikov https://github.com/sqshq
En particular su trabajo PiggyMetrics Publicado en http://my-piggymetrics.rhcloud.com/
La intencion de este repositorio es unicamente para efectos didaticos y con la finalidad de acelerar mediante la traduccion del proyecto el aprendaze de aquellos desarrolladores que con animos de incursionar en el nuevo paradigma de los microservicios buscan material practico y que el presente sirva como base conceptual practica.
Nada de lo traducido a contiuacion pretende de ninguna forma ser un plagio...
Aclarado el punto... a lo que venimos....
Una manera sencilla de llevar los gastos personales
Estos es una prueba de concepto proof-of-concept application, que demuestra como implementar El patron de arquitectura de Microservicios usando Spring Boot, Spring Cloud Y Docker con una pequeña y bonita interface, por cierto.
PiggyMetrics fue descompuesto en 3 microservicios core. Todos ellos son independientemente desplegables y organizados al rededor de un dominio de negocio.
Contiene la lógica general de validaciones de entrada de usuarios, ingresos y gastos, ahorros, y configuraciones de cuentas
Method | Path | Description | User authenticated | Available from UI |
---|---|---|---|---|
GET | /accounts/{account} | Get specified account data | ||
GET | /accounts/current | Get current account data | × | × |
GET | /accounts/demo | Get demo account data (pre-filled incomes/expenses items, etc) | × | |
PUT | /accounts/current | Save current account data | × | × |
POST | /accounts/ | Register new account | × |
Realiza cálculos principales sobre parámetros estadísticos, captura las series de tiempo para cada cuenta. Datapoint contiene valores normalizados a la moneda actual y el período de tiempo. Estos datos se utilizan para realizar un seguimiento del flujo de efectivo en el ciclo de vida de la cuenta.
Method | Path | Description | User authenticated | Available from UI |
---|---|---|---|---|
GET | /statistics/{account} | Get specified account statistics | ||
GET | /statistics/current | Get current account statistics | × | × |
GET | /statistics/demo | Get demo account statistics | × | |
PUT | /statistics/{account} | Create or update time series datapoint for specified account |
Almacena la información de la cuenta de los usuarios y configura las notificaciones (como recordar y respaldar la frecuencia). El worker programado recopila la información requerida de otros servicios y envía mensajes de correo electrónico a los clientes suscritos.
Method | Path | Description | User authenticated | Available from UI |
---|---|---|---|---|
GET | /notifications/settings/current | Get current account notification settings | × | × |
PUT | /notifications/settings/current | Save current account notification settings | × | × |
- Cada microservicio tiene su propia base de datos por lo que no hay forma de que se accesda en manera bypass a la persistencia directamente.
- Se uso MongoDB como base primaria para cada servicio esto lleva un sentido de hacer la persistencia poliglota en la arquitectura (elegir el tipo de base de datos acorde al requerimiento es la mejor opcion siempre).
- La comunicacion Service-to-service esta un poco simplificada: los microservices se hablan usando unicamente synchronous REST API. La practica cumun en el mundo real es el uso combinado de sincronos y asincronos
- Por ejemplo, Realizar sincronos los request GET para el retrieve de datos y usar enfoque asincronno por medio de Message broker para hacer operaciones de creates/update y asi poder desacoplar los services del buffer de mesajes. sin embargo esto lleva eventualmente a que tengamos la eventual consistency.
Hay un monton de patrones comunes en los sistemas distribuidos que prodrian ayudarnos a hacer que los servicios core descritos funcionen. Spring cloud nos da herramientas poderosas que se basen en aplicaciones spring boot para imprementar esos patrones, se cubriran a continuacion.
Spring Cloud Config es escalable horizontalmente y el servico de configuraciones para sistemas distribuidos. Se usa como capa de repositorio pluggable que actualmente soporta almacenamiento local, git y subversion.
Para este proyecto se usa native profile
, que simplifica la carga de los archivos del classpath local. Puede verse el directorio shared
directory en Config service resources.
Ahora cuando el servicio de Notification solicite su configuration, El Config service responde con shared/notification-service.yml
y shared/application.yml
(que son compartidos entre todos los clientes de la aplicacion).
Nada mas agregando la dependencia spring-cloud-starter-config
al cliente la autoconfiguration hara el resto
Ya no necesitas ninguna propiedad embebida en tu aplicacion. nada mas tenemos que configurar bootstrap.yml
con el nombre de la aplicacion y la url del Config service:
spring:
application:
name: notification-service
cloud:
config:
uri: http://config:8888
fail-fast: true
Por ejemplo el EmailService bean fue anotado con @RefreshScope
. que lo quiere decir que se puede cambiar del e-mail de notificacion el contenido y el asunto sin tener que reconfigurar y reiniciar el servicio de Notificacion de la aplication.
Lo primero es ambiar el properties requerido en el Config server. luego hacer un refresh al servicio de noticacion:
curl -H "Authorization: Bearer #token#" -XPOST http://127.0.0.1:8000/notifications/refresh
Tambien se puede usar el Repository webhooks to automate this process
- Hay algunas limitantes para el refresco dinamico.
@RefreshScope
no trabaja con clases@Configuration
y no afecta los metodos@Scheduled
- la propiedad
fail-fast
significac que la aplicacion Spring Boot application va a fallar inmediatamete si no esta conectado a un servicio Config Service. - Hay significativas security notes a seguir.
La responsabilidad de Authorization esta completamente delegada a un servidor separado que otorga OAuth2 tokens para servir los recursos del backend. Auth Server es usado por usuarios autorizados como tambien por comunicaciones machine-to-machine dentro del la perimetria.
En este proyecto se uso Password credentials
que da autorizacion de tipo usuario (ya que solo es usado por el front end de PiggyMetrics) y Client Credentials
da permisos a autorizacion microservices.
Spring Cloud Security provee de anotaciones y autoconfiguraciones para hacerlo realmente facil de implementar tanto del lado del server como del cliente, puedes aprender mas de su documentation y checha los detalles de configuracion en Auth Server code.
Del lado del cliente todo funciona exactamente igual que la tradicional forma basada en sesion autorizada. puedes hacer retrieve del objeto Principal
del request, checa el rol del user y otras cosas con control de acceso basado en una expresion y la anotacion @PreAuthorize
.
Cada cliente en PiggyMetrics (account-service, statistics-service, notification-service and browser) tiene un scope: server
para servicios backend y ui
- para el browser. Por lo que podemos tambien proteger controles de una acceso externo, por ejemplo:
@PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(value = "accounts/{name}", method = RequestMethod.GET)
public List<DataPoint> getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}
Como se puede apreciar hay 3 servicios core con un api expuesto al cliente, en un sistema del mundo este numero podria crecer muy rapidamente tanto como la complejidad del sistema mismo. Realmente miles de servicios pueden estar involucrados en el rendering de una webpage compleja.
En teoria un cliente podria hacer requests a cada microservicio directamente, pero obviamente, hay muchos retos y limitaciones a esta opcion, como por jemplo saber todos las direcciones de los endpoints, realizar requests http para cada lugar de informacion de manera separada, juntar el resultado para el cliente, Otro problea es los protocolos nada amigables web que debe usar el backend.
Usualmente el mejor enfoque es usar un API Gateway, Este es el unico endpoint para el sistema, usado par manejar request enrutandolos a los apropiados servicios o ligando los multiples servicios del backend y usando el patron aggregating the results.
Tambien pueden usar autenticacion, hayazgo y canary testing, service migration, static response handling, active traffic management.
Netflix "opensourcea" such an edge service, y ahora con Spring Cloud podemos habilitarlo con la anotacion @EnableZuulProxy
.
En este proyecto Se usa Zuul para almacenar contenido estatico (ui application) y para dirigir requests a los apropiados microservices. Aqui hay un ejemplo de como se configuro para los servicios de Notification el enrutamiento:
zuul:
routes:
notification-service:
path: /notifications/**
serviceId: notification-service
stripPrefix: false
Eso significa que todos los requests que comienzan con /notifications
seran enrutados al servicio Notification no hay direcciones hardcodeadas, como puedes ver Zuul usa mecanismo de Service discovery para localizar el servicio Notification service tambien instancia Circuit Breaker and Load Balancer, descrito a continuacion.
Otro muy comun conocido patron en la arquitectura es el Service discovery. este te deja detectar en la red automaticamente locaciones de servicios las cuales podrian tener direcciones asignadas dinamicamente debido a cuestiones de auto escalamiento, caidas y relanzamientos en algun servicio.
La parte clave del Service discovery es el Registry. Se usa Netflix Eureka en este projecto. Eureka es una buena implementeacion del patron para el client-side, cuando el cliente es responsable de determinar las ubicaciones de las instancias de servicio disponibles (utilizando el servidor del Registro) y las solicitudes de equilibrio de carga a través de ellas.
Con Spring Boot puedes facilmente construir Eureka Registry con la dependencia spring-cloud-starter-eureka-server
, la anotacion @EnableEurekaServer
y una simple configuracion del properties.
Client suporta la habilitacion con la anotacion @EnableDiscoveryClient
en el bootstrap.yml
con el nombre de la aplicacion:
spring:
application:
name: notification-service
Ahora que la aplicacion se levanto se registrara con Eureka Server y prove de meta-datos, tales como el host el puerto, indicador de salud URL, home page etc. Eureka recibe mensajes de heartbeat de cada una de las salidas de orugen de un service. si el heartbeat falla segun sea el timetable configurable, la instancia será removida del registry.
Además, Eureka proporciona una interfaz sencilla donde puede realizar un seguimiento de los servicios en ejecución y el número de instancias disponibles: http://localhost:8761
Netflix OSS provee de otro gran set de herramientas.
Ribbon es un balanceador de carga del lado del cliente que da mucho control sobre el comportamiento de clientes HTTP y TCP. Comparados con los tracionales balanceadores de carga, aqui no necesitas adicionar nada para cualquier invocacion over-the-wire - puedes contactar el servicio deseado dirsectamente.
Saliendo de la caja, es nativamente integrado con Spring Cloud y Service Discovery. Eureka Client provee una lista dinamica de servidores disponibles y con Ribbon podrias blancerlaros entre ellos.
Hystrix es la implementacion de un Circuit Breaker pattern, te da el ontrol sobre la latencia que y fallas del accesos a las dependncias sobre la red.
La idea princpar es parar las fallas en cascada en un entorno distribuido con un gran numero de microservices. Eso te ayuda a fallar rapido y que te recuperes loas mas rapido posible un importante aspecto de la tolerancia fallas del sistema que se auto sana.
Dentro del control de corto circuito, con Hystrix puedes agregar metodos de fallback method que te llaman a obtener un valor default en caso de que el principal comando falle.
Adicional, Hystrix genera metricas en executiones entrantes y latencia para cada uno de los comandos, con eso lo podemos usar para monitor system behavior.
Feign es un cliente HTTP declarativo que sin problemas se integra con Ribbon y Hystrix. En realidad con agregar la dependencia spring-cloud-starter-feign
y la anotacion @EnableFeignClients
tienes el conjunto completo de Load balancer, Circuit breaker y Http client con una configuracion sensible ready-to-go por default.
Aqui un ejemplo del serivioc de Cuenta:
@FeignClient(name = "statistics-service")
public interface StatisticsServiceClient {
@RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void updateStatistics(@PathVariable("accountName") String accountName, Account account);
}
- Todo lo que necesitas es solo una interface
- Puedes compartir
@RequestMapping
entreSpring MVC controller y metodos Feign - El ejemplo anterior solo especifica el id del servicio deseado desired service id -
statistics-service
, gracias al autodiscovery atravez de Eureka (pero obviamente tu puedes acceder a cualquier recurso con la url especifica)
En la configuracion de este proyecto cada microservicio con Hytrix aborda pone su estadistica a Turbine via Spring Cloud Bus (with AMQP broker). El proyecto Monitoring es una pequeña aplicacion Spring boot con Turbine y Hystrix Dashboard.
Ve el siguiente how to get it up and running.
Vamos a ver el comportamiento del sistema bajo carga:
El servicio de Account llama al servicio de Statistics service y le reposnde con una variedad de retrazos. El umbral de tiempo de respuesta se establece en 1 segundo.
Centralizar las trazas logs es muy util al momento de identificar problemas en estos ambientes distribuidos. Elasticsearch, Logstash y Kibana en conjunto nos dejan buscar y analizar logs, utilizacion y actividad de red listos para usar en configuracion docker descrita en este otro projecto.
Una configuración de seguridad avanzada está más allá del alcance de este proyecto de prueba de concepto. Para una simulación más realista de un sistema real, considere utilizar https, almacén de claves JCE para cifrar las contraseñas de Microservices y el contenido de las propiedades del servidor de configuración (consulte [documentación](http://cloud.spring.io/spring-cloud-config/spring- cloud-config.html #_security) para más detalles).
Deployar microservicios cons sus interdependencias es muchas mas compeljo que el deploy de una aplicacion monolitica. Es importante tener toda la infraestructura completamente automatizada. Podemos lograrla scon los benedicios del enfoque de la Continuous Delivery:
- La habilidad de hacer release en cualquier momento
- Cualquier build se puede convertir en un release
- Build de artifactos una vez - deploy Cuando los necesites
Aqui esta un workflow simple de un Continuous Delivery que se ha implementado en este projecto:
En esta configuracion, Travis CI genera imágenes etiquetadas apartir de un push a git exitoso. Por lo tanto, siempre hay última
imagen para cada microservice en Docker Hub y imágenes más antiguas, etiquetadas con el hash del commit. Es fácil desplegar cualquiera de ellos y retroceder rápidamente, si es necesario.
Acuerdate que estas inciciando 8 aplicaciones Spring Boot, 4 instancias MongoDB y RabbitMq.
Asegurate de tener 4 Gb
RAM habilitadas en tu maquina. puedes correr solamente los servicios vitales: Gateway, Registry, Config, Auth Service and Account Service.
- Instala Docker y Docker Compose.
- haz un Export de las variables de entorno:
CONFIG_SERVICE_PASSWORD
,NOTIFICATION_SERVICE_PASSWORD
,STATISTICS_SERVICE_PASSWORD
,ACCOUNT_SERVICE_PASSWORD
,MONGODB_PASSWORD
(asegurate que esten expuestas:printenv
) - Asegurate de construir el proyecto:
mvn package [-DskipTests]
De esta forma todas las imagenes mas recientes (latest tag)seran "pulleadas" de Docker Hub.
Nada mas copia docker-compose.yml
y da un docker-compose up
Si quisieras construir tu mismo imagenes (Con algunso cambios al codigo por ejemplo), debes clonar todo el repositorio y construir los artefactos con maven y luego correr
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
docker-compose.dev.yml
hereda de docker-compose.yml
con la posibilidad adicional de contruir imagenes localmente y exponer todos los purtos de los contenedores para un entorno mas a modo.
- http://localhost:80 - Gateway
- http://localhost:8761 - Eureka Dashboard
- http://localhost:9000/hystrix - Hystrix Dashboard (paste Turbine stream link on the form)
- http://localhost:8989 - Turbine stream (source for the Hystrix Dashboard)
- http://localhost:15672 - RabbitMq management (default login/password: guest/guest)
Todas las aplicaciones Spring Boot requieran estar corriendo Config Server para hacer el startup. Pero podemos levantarlas todos los contenedores simultaneamente por la opcion depends_on
de docker-compose.
Tambien el mecanismo Service Discovery necesita de algo de tiempo para que las applications levanten. Cualquier service que no está disponible para ser descubierto por los clientes hasta que la instancia, el servidor Eureka y el cliente tengan todos los mismos metadatos en su caché local, por lo que podría tardar 3 heartbeats. El tiempo predeterminado del latido del corazón es de 30 segundos.
PiggyMetrics es open source y apreciaria mucho tu ayuda. Siente libre de contactarme para cualquier duda o comentario.