Tích hợp và vận hành hệ thống giám sát chuỗi gọi dịch vụ với Spring Cloud Sleuth và Zipkin

Yêu cầu thực tế

Khi xây dựng kiến trúc vi dịch vụ, hai vấn đề thường gặp là:

  • Cần xác định nhanh vị trí lỗi khi một yêu cầu HTTP xuyên suốt nhiều service trả về lỗi (ví dụ: HTTP 500).
  • Cần phân tích điểm nghẽn hiệu năng trong chuỗi gọi liên dịch vụ — ví dụ: một service mất 2s để xử lý trong khi các service khác chỉ mất vài chục mili giây.

Cơ chế hoạt động tổng quan

Hệ thống sử dụng mô hình client-server:

  • Client: Spring Cloud Sleuth nhúng vào từng service để tự động gắn thẻ (trace ID, span ID), ghi log phân tán và gửi dữ liệu tới server.
  • Server: Zipkin nhận dữ liệu từ các client, lưu trữ (bộ nhớ hoặc Elasticsearch/Cassandra), và cung cấp giao diện web để truy vấn, lọc, trực quan hóa chuỗi gọi và cảnh báo lỗi.

Các bước triển khai

1. Thêm phụ thuộc Maven

Chỉ cần khai báo spring-cloud-starter-zipkin. Thư viện này đã bao gồm spring-cloud-starter-sleuth, nên không cần khai báo riêng:

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

2. Cấu hình ứng dụng

Trong application.yml:

spring:
  zipkin:
    base-url: http://localhost:9411
    sender:
      type: web # Gửi đồng bộ qua HTTP (mặc định)
  sleuth:
    sampler:
      probability: 1.0 # Mẫu 100% cho môi trường dev

logging:
  level:
    org.springframework.cloud.sleuth: DEBUG
    org.springframework.web.servlet.DispatcherServlet: WARN

3. Khởi chạy Zipkin Server

Cách 1 – Dùng Docker (khuyến nghị):

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

Cách 2 – Chạy jar độc lập:

curl -o zipkin.jar 'https://repo1.maven.org/maven2/io/zipkin/zipkin-server/2.24.0/zipkin-server-2.24.0-exec.jar'
java -jar zipkin.jar

4. Kiểm tra tích hợp

Sau khi khởi động service, kiểm tra log đầu ra. Nếu xuất hiện dòng tương tự:

[order-service,1a2b3c4d5e6f7890,1a2b3c4d5e6f7890,true]

→ Điều đó chứng tỏ Sleuth đã tạo thành công trace ID và span ID. Truy cập http://localhost:9411 để xem dashboard Zipkin.

Một số sự cố thường gặp và cách khắc phục

Vấn đề 1: Lỗi khởi tạo bean do visibility của phương thức @Scheduled

Lỗi điển hình:

Caused by: java.lang.IllegalStateException: Need to invoke method 'fetchMetrics' found on proxy... but cannot be delegated to target bean. Switch its visibility to package or protected.

Nguyên nhân: Phương thức được đánh dấu @Scheduled có phạm vi private hoặc default, khiến Spring AOP không thể tạo proxy để can thiệp.

Cách sửa: Đổi thành public:

@Component
public class MetricPoller {
  @Scheduled(fixedDelay = 30_000)
  public void fetchMetrics() { // ← đổi từ private sang public
    // ...
  }
}

Vấn đề 2: Lỗi timeout Redis trong @PostConstruct

Lỗi xảy ra khi một bean dùng @PostConstruct thực hiện thao tác mạng (ví dụ: xóa key trên Redis) ngay sau khi khởi tạo, nhưng lúc đó Sleuth chưa sẵn sàng hoặc kết nối Redis chưa ổn định.

Giải pháp bền vững: Di chuyển logic khởi tạo sang ApplicationRunner hoặc CommandLineRunner, đảm bảo chạy sau toàn bộ context đã được load:

@Component
public class CacheInitializer implements ApplicationRunner {
  private final RedisTemplate<String, Object> redisTemplate;

  public CacheInitializer(RedisTemplate<String, Object> redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  @Override
  public void run(ApplicationArguments args) throws Exception {
    redisTemplate.delete("resource_role_cache"); // ← chạy an toàn sau khi Sleuth đã khởi động
  }
}

Sử dụng Zipkin để điều tra sự cố

Sau khi hệ thống hoạt động, truy cập giao diện Zipkin tại http://localhost:9411.

Quy trình điều tra lỗi 500:

  1. Chọn Look Up → nhập path API (ví dụ: /api/v1/orders) → nhấn Find Traces.
  2. Lọc theo cột Errors để hiển thị các trace có span chứa exception.
  3. Chọn một trace → xem chi tiết từng span: thời gian thực thi, HTTP status, exception message, và call stack (nếu được cấu hình logback để ghi stack vào span tag).

Ví dụ: nếu service payment-service trả về NullPointerException khi gọi tới user-service, bạn sẽ thấy rõ span tương ứng có tag error=java.lang.NullPointerException và thời gian chờ vượt ngưỡng.

Thẻ: spring-cloud-sleuth zipkin distributed-tracing Microservices observability

Đăng vào ngày 21 tháng 5 lúc 23:23