Spring Cloud: seguimiento distribuido con Sleuth

En este artículo, le presentaremos Spring Cloud Sleuth, que es un marco de seguimiento distribuido para una arquitectura de microservicio en el ecosistema Spring....

Visión general

En este artículo, te presentaremos Detective de nubes de primavera, que es un framework de rastreo distribuido para una arquitectura de microservicios en el ecosistema Spring. .

En una arquitectura típica de microservicios, tenemos muchas aplicaciones pequeñas implementadas por separado y, a menudo, necesitan comunicarse entre sí. Uno de los desafíos que enfrentan los desarrolladores es rastrear una solicitud completa de registros para depurar o verificar la latencia en los servicios posteriores.

Para aumentar aún más la complejidad, algunos servicios pueden tener varias instancias en ejecución. Es difícil rastrear registros de solicitudes particulares en múltiples servicios, especialmente si un servicio en particular tiene muchas instancias.

Spring Cloud Sleuth agrega automáticamente algunos rastros/metadatos a sus registros y comunicación entre servicios (a través de encabezados de solicitud), por lo que es fácil rastrear una solicitud a través de agregadores de registros como [Zipkins] (https://zipkin.io/), ELK, etc.

Este artículo asume que ya tiene conocimiento de los componentes básicos de Spring Cloud. Hemos publicado varios artículos que cubren Spring Cloud si desea leer más:

Configuración

Para demostrar el concepto de seguimiento, utilizaremos algunos servicios:

  • Servidor Eureka: Actúa como un registro de servicios y se ejecuta en el puerto 8761.
  • Servicio de direcciones: un servicio REST simple que tiene un punto final único de /address/{customerId} y se ejecuta en el puerto 8070.
  • Servicio al cliente: un servicio REST simple que tiene un punto final único de /customer/{customerId} y se ejecuta en el puerto 8060.
  • Servicio de portal: un servicio REST simple que tiene un punto final único de /fullDetails/{customerId} y se ejecuta en el puerto 8050. Este servicio llama internamente a address-service y customer-service para obtener datos y los combina antes de la respuesta.
  • Puerta de enlace: único punto de entrada a nuestra arquitectura de microservicios, compilada con Spring Cloud Gateway y ejecutándose en el puerto 8080.

flujo-solicitud-de-detective-nube-de-primavera

Y así es como se ve el servidor Eureka cuando todo el servicio se está ejecutando:

spring-cloud-sleuth-eureka

Veamos qué está escrito en cada clase de controlador, comenzando desde AddressController del address-service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@RestController  
@RequestMapping(value = "/address")  
public class AddressController {  
  
    private static Logger log = LoggerFactory.getLogger(AddressController.class);  
  
    @GetMapping(value = "/{customerId}")  
    public String address(@PathVariable(name = "customerId", required = true) long customerId) {  
        log.info("GET /address/"+customerId);  
        return "Address of id="+customerId;  
    }  
}

CustomerController de servicio-al-cliente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@RestController  
@RequestMapping(value = "/customer")  
public class CustomerController {  
  
    private static Logger log = LoggerFactory.getLogger(CustomerController.class);  
  
    @GetMapping(value = "/{customerId}")  
    public String address(@PathVariable(name = "customerId", required = true) long customerId){  
        log.info("GET /customer/"+customerId);  
        return "Customer details of id="+customerId;  
    }  
}

PortalController de portal-service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@RestController
public class PortalController {

    private static Logger log = LoggerFactory.getLogger(PortalController.class);

    @Autowired
    RestTemplate restTemplate;

    @GetMapping(value = "/fullDetails/{customerId}")
    public String address(@PathVariable(name = "customerId", required = true) long customerId) {
        log.info("GET /fullDetails/" + customerId);

        String customerResponse = restTemplate.getForObject("http://customer-service/customer/" + customerId, String.class);
        String addressResponse = restTemplate.getForObject("http://address-service/address/" + customerId, String.class);

        return customerResponse + "<br>" + addressResponse;
    }
}

Para verificar, ejecutemos el punto final portal-service a través de gateway navegando su navegador a http://localhost:8080/portal-service/fullDetails/12. Debería ver algo como esto:

url-prueba-detective-nube-primavera

Ahora, imagine rastrear estos registros en diferentes servidores. Además, incluso si tiene estos archivos de registro en una ubicación común y tiene un agregador de registros, sería difícil encontrar un seguimiento completo de una solicitud entre varios servicios en algún momento.

Adición de Spring Cloud Sleuth

Spring Cloud Sleuth agrega ID únicos a sus registros, que permanecen iguales entre muchos microservicios y pueden ser utilizados por agregadores de registros comunes para ver cómo fluye una solicitud.

Para agregar esta funcionalidad, necesitamos agregar una dependencia en el archivo pom.xml de cada servicio descendente:

1
2
3
4
<dependency>  
 <groupId>org.springframework.cloud</groupId>  
 <artifactId>spring-cloud-starter-sleuth</artifactId>  
</dependency>

Reinicie toda la aplicación y presione el punto final http://localhost:8080/portal-service/fullDetails/12 nuevamente y verifique los registros de cada servicio.

Registros del ‘servicio del portal’:

primavera-nube-detective-logs1

Registros del ‘servicio de direcciones’:

primavera-nube-detective-logs2

Registros de Servicio al cliente:

primavera-nube-detective-logs3

Spring Cloud Sleuth agrega dos tipos de ID a su registro:

  • Id. de seguimiento: un Id. único que permanece igual durante toda la solicitud que contiene varios microservicios.
  • Span Id: una identificación única por microservicio.

Básicamente, un Trace ID contendrá varios Span ID que las herramientas de agregación de registros pueden usar fácilmente.

Sleuth no solo agrega estos ID a nuestros registros, sino que también los propaga a las próximas llamadas de servicio (basadas en HTTP o MQ). Además, puede enviar registros de muestra aleatorios a aplicaciones externas como Zipkins desde el primer momento.

Agregación de registros con Zipkins

Zipkins es un sistema de seguimiento distribuido que generalmente se usa para solucionar problemas de latencia en las arquitecturas de servicios.

Para ejecutar un servidor Zipkin, puede seguir una guía rápida y sencilla aquí.

Usé la forma de Java para ejecutarlo, ejecutando los comandos:

1
2
$ curl -sSL https://zipkin.io/quickstart.sh | bash -s
$ java -jar zipkin.jar

Aunque también puedes ejecutarlo a través de Docker o directamente desde el código fuente.

De forma predeterminada, el servidor Zipkin se ejecutará en el puerto 9411. Navegue su navegador a http://localhost:9411/zipkin/, para acceder a su página de inicio:

primavera-nube-sleuth-zipkin-home-page

Integración de Sleuth con Zipkins

Ahora, tenemos que decirle a Sleuth que envíe datos al servidor Zipkin. Primero necesitamos agregar otra dependencia al archivo pom.xml de cada servicio:

1
2
3
4
<dependency>  
 <groupId>org.springframework.cloud</groupId>  
 <artifactId>spring-cloud-starter-zipkin</artifactId>  
</dependency>

Después de esto, debemos agregar las siguientes propiedades en el archivo application.properties de cada servicio:

1
2
spring.sleuth.sampler.probability=100  
spring.zipkin.baseUrl= http://localhost:9411/

La propiedad spring.zipkin.baseUrl le dice a Spring y Sleuth dónde enviar los datos. Además, de forma predeterminada, Spring Cloud Sleuth establece todos los tramos como no exportables. Esto significa que estos rastros (Trace Id y Span Id) aparecen en los registros pero no se exportan a otra tienda remota como Zipkin.

Para exportar intervalos al servidor Zipkin, debemos establecer una frecuencia de muestreo mediante spring.sleuth.sampler.probability. Un valor de 100 significa que todos los intervalos también se enviarán al servidor Zipkin.

Ahora, reiniciemos todas las aplicaciones nuevamente y presionemos el punto final http://localhost:8080/portal-service/fullDetails/12 nuevamente.

Ahora, en la página de inicio de Zipkin en http://localhost:9411/zipkin/, haga clic en "Buscar rastros":

spring-cloud-sleuth-zipkin-find-traces

Al hacer clic en un seguimiento, se nos dirigirá a su página de detalles:

primavera-nube-detective-zipkin-busca-trazas-detalles

Arriba podemos ver que la solicitud en general tomó alrededor de 16 ms y un árbol que muestra el tiempo que tomó cada servicio.

Normalmente, para visualizar registros con fines de depuración, usamos la pila de ELK. Para integrarlo con Sleuth podemos seguir la explicación aquí.

Conclusión

En este artículo, hemos cubierto cómo usar Spring Cloud Sleuth en nuestra aplicación de microservicio basada en Spring existente. Vimos cómo es útil para el seguimiento de registros de una sola solicitud que abarca varios servicios. También lo integramos con un servidor Zipkin para ver la latencia de cada subservicio en la solicitud y respuesta general.

Como siempre, el código de los ejemplos utilizados en este artículo se puede encontrar en Github.