0%
Tracing Distribué

Tracing

Tracing distribué avec Sleuth et Zipkin

10-15 min

Tracing Distribué avec Sleuth et Zipkin

Le tracing distribué est essentiel pour comprendre et déboguer les interactions entre les microservices. Spring Cloud Sleuth et Zipkin permettent de suivre les requêtes à travers l’ensemble de l’architecture.

1. Configuration de Base

1.1 Ajout des Dépendances

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>

1.2 Configuration de Sleuth

# application.yml
spring:
  application:
    name: product-service
  sleuth:
    otel:
      config:
        trace-id-ratio-based: 1.0
    propagation:
      type: w3c
    sampler:
      probability: 1.0
  zipkin:
    base-url: http://localhost:9411
    sender:
      type: web

2. Configuration de Zipkin

2.1 Installation de Zipkin

docker run -d -p 9411:9411 openzipkin/zipkin

2.2 Configuration des Services

# application.yml
spring:
  zipkin:
    base-url: http://localhost:9411
    sender:
      type: web
    service:
      name: ${spring.application.name}

3. Implémentation du Tracing

3.1 Configuration du Tracer

@Configuration
public class TracingConfig {
    @Bean
    public Tracer tracer() {
        return new Tracer.Builder()
            .withSampler(Sampler.ALWAYS_SAMPLE)
            .build();
    }
}

3.2 Utilisation dans les Services

@Service
@Slf4j
public class ProductService {
    private final Tracer tracer;
    private final ProductRepository productRepository;

    public ProductService(Tracer tracer, ProductRepository productRepository) {
        this.tracer = tracer;
        this.productRepository = productRepository;
    }

    public Product getProduct(Long id) {
        Span span = tracer.nextSpan().name("getProduct");
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
            log.info("Fetching product with id: {}", id);
            return productRepository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException(id));
        } finally {
            span.finish();
        }
    }
}

4. Intégration avec les Clients HTTP

4.1 Configuration du RestTemplate

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(Tracer tracer) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(Collections.singletonList(
            new TracingRestTemplateInterceptor(tracer)
        ));
        return restTemplate;
    }
}

4.2 Intercepteur de Tracing

public class TracingRestTemplateInterceptor implements ClientHttpRequestInterceptor {
    private final Tracer tracer;

    public TracingRestTemplateInterceptor(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
            ClientHttpRequestInterceptor.ClientHttpRequestExecution execution) throws IOException {
        Span span = tracer.nextSpan().name("http-request");
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
            request.getHeaders().add("X-Trace-ID", span.context().traceId());
            return execution.execute(request, body);
        } finally {
            span.finish();
        }
    }
}

5. Monitoring et Visualisation

5.1 Configuration des Métriques

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

5.2 Métriques Personnalisées

@Component
public class TracingMetrics {
    private final MeterRegistry registry;
    private final Tracer tracer;

    public TracingMetrics(MeterRegistry registry, Tracer tracer) {
        this.registry = registry;
        this.tracer = tracer;
        registerMetrics();
    }

    private void registerMetrics() {
        Gauge.builder("tracing.active.spans", tracer, this::getActiveSpans)
            .description("Number of active spans")
            .register(registry);

        Counter.builder("tracing.spans")
            .description("Number of spans")
            .tag("type", "http")
            .register(registry);
    }

    private double getActiveSpans(Tracer tracer) {
        return tracer.currentSpan() != null ? 1 : 0;
    }
}

6. Tests

6.1 Test d’Intégration

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TracingIntegrationTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void whenGetProduct_thenTraceIsGenerated() {
        // Act
        ResponseEntity<Product> response = restTemplate.getForEntity(
            "/api/products/1", Product.class);

        // Assert
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertNotNull(response.getBody());
        assertTrue(response.getHeaders().containsKey("X-Trace-ID"));
    }
}

6.2 Test du Tracer

@SpringBootTest
class TracerTest {
    @Autowired
    private Tracer tracer;

    @Test
    void whenCreateSpan_thenSpanIsCreated() {
        // Act
        Span span = tracer.nextSpan().name("test-span");
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
            assertNotNull(span);
            assertNotNull(span.context());
            assertNotNull(span.context().traceId());
        } finally {
            span.finish();
        }
    }
}

Conclusion

Le tracing distribué avec Sleuth et Zipkin offre plusieurs avantages :

  • Visualisation complète des interactions entre services
  • Identification rapide des goulots d’étranglement
  • Débogage facilité des problèmes distribués
  • Métriques détaillées sur les performances
  • Intégration native avec Spring Cloud

Dans le prochain tutoriel, nous verrons comment sécuriser nos microservices avec Spring Security et OAuth2.

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !