Exponer las métricas de Hystrix en Grafana con Prometheus de una aplicación Spring Boot

Escrito por el .
java planeta-codigo programacion software spring
Enlace permanente Comentarios

Java

Spring

Hystrix es una implementación del patrón circuit breaker para hacer que un servicio sea tolerante fallos cuando aquellos que utiliza fallan. Es conveniente tener una herramienta de monitorización para conocer el estado del sistema y actuar pronto o conocer si el comportamiento del sistema es diferente al hacer algún cambio. Hystrix proporciona varios datos como el número de peticiones realizadas, cuantas han fallado o cual es el estado del patrón circuit breaker. Prometheus es una herramienta de monitorización que recoge las métricas de los servicios de forma periódica y las almacena para una consulta posterior, Grafana es otra herramienta de monitorización que permite visualizar en gráficas las métricas almacenadas en Prometheus y observar los valores a lo largo del tiempo.

En el artículo Tolerancia a fallos en un cliente de microservicio con Spring Cloud Netflix y Hystrix explicaba como crear un servicio de Spring que implementa el patrón circuit breaker con Hystrix y en el artículo Monitorizar una aplicación Java con Spring Boot, Micrometer, Prometheus y Grafana explicaba como exportar las métricas de Spring Boot Actuator a Prometheus y como crear gráficas en Grafana.

Hystrix ofrece un dashboard algo espartano con los datos de Hystrix de la propia aplicación. Los datos de las métricas de Hystrix por defecto no se exponen en Spring Boot Actuator pero se pueden añadir creando un bean HystrixMetricsBinder en la configuración de Spring.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.github.picodotdev.blogbitix.springcloud.client;

...

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
@EnableHystrixDashboard
public class Main implements CommandLineRunner {

  ...

	@Bean
	HystrixMetricsBinder hystrixMetricsBinder() {
		return new HystrixMetricsBinder();
	}
    
  ...

	public static void main(String[] args) {
		SpringApplication application = new SpringApplication(Main.class);
		application.setApplicationContextClass(AnnotationConfigApplicationContext.class);
		SpringApplication.run(Main.class, args);
	}
}
Main.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
...

dependencies {
    // Spring
    def excludeSpringBootStarterLogging = { exclude(group: 'org.springframework.boot', module: 'spring-boot-starter-logging') }
    compile('org.springframework.boot:spring-boot-starter', excludeSpringBootStarterLogging)
    compile('org.springframework.boot:spring-boot-starter-web', excludeSpringBootStarterLogging)
    compile('org.springframework.boot:spring-boot-starter-log4j2', excludeSpringBootStarterLogging)
    compile('org.springframework.boot:spring-boot-starter-actuator', excludeSpringBootStarterLogging)
    compile('org.springframework.cloud:spring-cloud-starter-config', excludeSpringBootStarterLogging)
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client', excludeSpringBootStarterLogging)
    compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon', excludeSpringBootStarterLogging)
    compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix', excludeSpringBootStarterLogging)
    compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard', excludeSpringBootStarterLogging)
    compile('io.micrometer:micrometer-registry-prometheus:1.0.8')

    ...
}
build.gradle

Una vez hecho esto Spring en el endpoint /actuator/metrics se exponen las métricas de Hystrix, si además se configura Spring añadiendo la dependencia io.micrometer:micrometer-registry-prometheus para exponer las métricas en el formato para que Prometheus las recolecta también se añaden en el endpoint /actuator/prometheus.

1
2
3
4
5
6
7
8
$ curl http://client.127.0.0.1.xip.io:8095/actuator/metrics
$ curl http://client.127.0.0.1.xip.io:8095/actuator/metrics/hystrix.requests
$ curl http://client.127.0.0.1.xip.io:8095/actuator/metrics/hystrix.circuit.breaker.open
$ curl http://client.127.0.0.1.xip.io:8095/actuator/metrics/hystrix.fallback
$ http://client.127.0.0.1.xip.io:8095/actuator/metrics/hystrix.latency.total
$ http://client.127.0.0.1.xip.io:8095/actuator/metrics/hystrix.errors

$ curl http://client.127.0.0.1.xip.io:8095/actuator/prometheus
metrics-endpoints.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "names": [
    ...
    "hystrix.requests",
    "hystrix.command.other",
    "hystrix.circuit.breaker.open",
    "hystrix.fallback",
    "hystrix.latency.execution",
    "hystrix.execution",
    "hystrix.latency.total",
    "hystrix.threadpool.concurrent.execution.rolling.max",
    "hystrix.errors",
    "hystrix.threadpool.concurrent.execution.current",
    ...
  ]
}
actuator-metrics.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
# HELP hystrix_threadpool_concurrent_execution_current  
# TYPE hystrix_threadpool_concurrent_execution_current gauge
hystrix_threadpool_concurrent_execution_current{group="ClientService",key="get",threadpool="ClientService",} 0.0
# HELP hystrix_requests_total  
# TYPE hystrix_requests_total counter
hystrix_requests_total{group="ClientService",key="get",} 137.0
# HELP hystrix_circuit_breaker_open  
# TYPE hystrix_circuit_breaker_open gauge
hystrix_circuit_breaker_open{group="ClientService",key="get",} 0.0
# HELP hystrix_latency_execution_seconds_max  
# TYPE hystrix_latency_execution_seconds_max gauge
hystrix_latency_execution_seconds_max{group="ClientService",key="get",} 0.472
# HELP hystrix_latency_execution_seconds  
# TYPE hystrix_latency_execution_seconds summary
hystrix_latency_execution_seconds_count{group="ClientService",key="get",} 137.0
hystrix_latency_execution_seconds_sum{group="ClientService",key="get",} 2.839
# HELP hystrix_execution_total Execution results. See https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring#command-execution-event-types-comnetflixhystrixhystrixeventtype for type definitions
# TYPE hystrix_execution_total counter
hystrix_execution_total{event="emit",group="ClientService",key="get",} 0.0
hystrix_execution_total{event="success",group="ClientService",key="get",} 137.0
hystrix_execution_total{event="failure",group="ClientService",key="get",} 0.0
hystrix_execution_total{event="timeout",group="ClientService",key="get",} 0.0
hystrix_execution_total{event="bad_request",group="ClientService",key="get",} 0.0
hystrix_execution_total{event="short_circuited",group="ClientService",key="get",} 0.0
hystrix_execution_total{event="thread_pool_rejected",group="ClientService",key="get",} 0.0
hystrix_execution_total{event="semaphore_rejected",group="ClientService",key="get",} 0.0
# HELP hystrix_threadpool_concurrent_execution_rolling_max  
# TYPE hystrix_threadpool_concurrent_execution_rolling_max gauge
hystrix_threadpool_concurrent_execution_rolling_max{group="ClientService",key="get",threadpool="ClientService",} 0.0
# HELP hystrix_command_other_total Other execution results. See https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring#other-command-event-types-comnetflixhystrixhystrixeventtype for type definitions
# TYPE hystrix_command_other_total counter
hystrix_command_other_total{event="exception_thrown",group="ClientService",key="get",} 0.0
hystrix_command_other_total{event="response_from_cache",group="ClientService",key="get",} 0.0
hystrix_command_other_total{event="cancelled",group="ClientService",key="get",} 0.0
hystrix_command_other_total{event="collapsed",group="ClientService",key="get",} 0.0
hystrix_command_other_total{event="command_max_active",group="ClientService",key="get",} 0.0
# HELP hystrix_latency_total_seconds_max  
# TYPE hystrix_latency_total_seconds_max gauge
hystrix_latency_total_seconds_max{group="ClientService",key="get",} 0.474
# HELP hystrix_latency_total_seconds  
# TYPE hystrix_latency_total_seconds summary
hystrix_latency_total_seconds_count{group="ClientService",key="get",} 137.0
hystrix_latency_total_seconds_sum{group="ClientService",key="get",} 2.857
# HELP hystrix_errors_total  
# TYPE hystrix_errors_total counter
hystrix_errors_total{group="ClientService",key="get",} 0.0
# HELP hystrix_fallback_total Fallback execution results. See https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring#command-fallback-event-types-comnetflixhystrixhystrixeventtype for type definitions
# TYPE hystrix_fallback_total counter
hystrix_fallback_total{event="fallback_emit",group="ClientService",key="get",} 0.0
hystrix_fallback_total{event="fallback_success",group="ClientService",key="get",} 0.0
hystrix_fallback_total{event="fallback_failure",group="ClientService",key="get",} 0.0
hystrix_fallback_total{event="fallback_rejection",group="ClientService",key="get",} 0.0
hystrix_fallback_total{event="fallback_missing",group="ClientService",key="get",} 0.0
...
actuator-prometheus.prometheus

Con estas métricas recolectadas por Prometheus se pueden visualizar en gráficas por Grafana. Hay algunos paneles de Grafana para Hystrix como el 7145 pero que necesitan ser adaptados según la nomenclatura de las propiedades expuestas por Spring Boot. En este caso se monitoriza el número de peticiones realizadas, el tiempo de latencia, si los circuitos están abiertos, los fallos, éxitos y tiemouts así como el estado de los thread pools que utiliza Hystrix para realizar las peticiones de un cliente a un servicio.

Panel de Grafana pàra métricas de Hystrix

Panel de Grafana para métricas de Hystrix

Exponer las métricas en una aplicación de Spring Boot para Prometheus es muy sencillo y con Grafana se puede observar el estado del sistema de forma tan detallada como lo sean las métricas expuestas por la aplicación. Por defecto Spring Boot ya expone una buena cantidad de métricas del estado del servicio como uso de CPU, memoria, hilos o recolector de basura.

1
2
3
4
$ ./gradlew discoveryserver:run --args="--port=8761"
$ ./gradlew configserver:run --args="--port=8090"
$ ./gradlew service:run --args="--port=8080"
$ ./gradlew client:run
gradle-run.sh
Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradle-run.sh


Este artículo forma parte de la serie spring-cloud:

  1. Datos de sesión externalizados con Spring Session
  2. Aplicación Java autocontenida con Spring Boot
  3. Configuración de una aplicación en diferentes entornos con Spring Cloud Config
  4. Información y métricas de la aplicación con Spring Boot Actuator
  5. Registro y descubrimiento de servicios con Spring Cloud y Consul
  6. Aplicaciones basadas en microservicios
  7. Registro y descubrimiento de servicios con Spring Cloud Netflix
  8. Recargar sin reiniciar la configuración de una aplicación Spring Boot con Spring Cloud Config
  9. Almacenar cifrados los valores de configuración sensibles en Spring Cloud Config
  10. Tolerancia a fallos en un cliente de microservicio con Spring Cloud Netflix y Hystrix
  11. Balanceo de carga y resiliencia en un microservicio con Spring Cloud Netflix y Ribbon
  12. Proxy para microservicios con Spring Cloud Netflix y Zuul
  13. Monitorizar una aplicación Java de Spring Boot con Micrometer, Prometheus y Grafana
  14. Exponer las métricas de Hystrix en Grafana con Prometheus de una aplicación Spring Boot
  15. Servidor OAuth, gateway y servicio REST utilizando tokens JWT con Spring
  16. Trazabilidad en microservicios con Spring Cloud Sleuth
  17. Implementar tolerancia a fallos con Resilience4j
  18. Iniciar una aplicación de Spring Boot en un puerto aleatorio
  19. Utilizar credenciales de conexión a la base de datos generadas por Vault en una aplicación de Spring
  20. Microservicios con Spring Cloud, Consul, Nomad y Traefik
  21. Trazabilidad en servicios distribuidos con Sleuth y Zipkin
  22. Configuración de una aplicación con Spring Boot y configuración centralizada con Spring Cloud Config
Comparte el artículo: