Sentinel Toàn Diện: Cấu Hình và Sử Dụng Trong Ứng Dụng Java

Cấu trúc Dự án Minh Họa

sentinel-integration/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── service/
│   │   │           ├── sentinel/
│   │   │           │   ├── FlowControlSetup.java
│   │   │           │   ├── CircuitBreakerSetup.java
│   │   │           │   └── SentinelExceptionHandler.java
│   │   │           ├── web/
│   │   │           │   └── ApiController.java
│   │   │           └── AppLauncher.java
│   │   └── resources/
│   │       └── application.yml
    

Triển Khai Chi Tiết

1. Lớp Khởi Tạo Ứng Dụng (AppLauncher.java)

@SpringBootApplication
public class AppLauncher {
    public static void main(String[] args) {
        SpringApplication.run(AppLauncher.class, args);
    }
}

2. Thiết Lập Quy Tắc Giới Hạn Lưu Lượng (FlowControlSetup.java)

@Configuration
public class FlowControlSetup implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        List<FlowRule> flowRules = new ArrayList<>();

        // Quy tắc cơ bản: giới hạn QPS cho API đơn giản
        FlowRule basicRule = new FlowRule();
        basicRule.setResource("apiSimpleCall");
        basicRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        basicRule.setCount(3); // Chỉ cho phép 3 yêu cầu mỗi giây
        basicRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);

        // Quy tắc theo tham số: giới hạn tần suất theo userId
        FlowRule paramRule = new FlowRule();
        paramRule.setResource("apiUserSpecific");
        paramRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        paramRule.setCount(1);          // Mỗi người dùng chỉ được gọi 1 lần/giây
        paramRule.setParamIdx(0);       // Áp dụng cho tham số đầu tiên

        flowRules.add(basicRule);
        flowRules.add(paramRule);

        FlowRuleManager.loadRules(flowRules);
    }
}

3. Cấu Hình Ngắt Mạch (CircuitBreakerSetup.java)

@Configuration
public class CircuitBreakerSetup implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        List<DegradeRule> degradeRules = new ArrayList<>();

        DegradeRule rule = new DegradeRule();
        rule.setResource("unstableServiceCall");
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        rule.setCount(4);           // Ngắt mạch nếu có 4 ngoại lệ
        rule.setTimeWindow(15);     // Thời gian chờ phục hồi: 15 giây

        degradeRules.add(rule);
        DegradeRuleManager.loadRules(degradeRules);
    }
}

4. Bộ Xử Lý API (ApiController.java)

@RestController
@RequestMapping("/api")
public class ApiController {

    private final Random faultInjector = new Random();

    // Gọi dịch vụ ổn định - bị giới hạn QPS
    @GetMapping("/simple")
    @SentinelResource(
        value = "apiSimpleCall",
        blockHandler = "onRequestBlocked"
    )
    public String simpleEndpoint() {
        return "Thành công: Yêu cầu được xử lý";
    }

    // Gọi cá nhân hóa - giới hạn theo người dùng
    @GetMapping("/user/{id}")
    @SentinelResource(
        value = "apiUserSpecific",
        blockHandler = "onUserRateLimited"
    )
    public String userEndpoint(@PathVariable Long id) {
        return "Xin chào người dùng " + id;
    }

    // Dịch vụ không ổn định - có thể kích hoạt ngắt mạch
    @GetMapping("/faulty")
    @SentinelResource(
        value = "unstableServiceCall",
        blockHandler = "onCircuitOpen",
        fallback = "serviceFallback"
    )
    public String faultyEndpoint() {
        if (faultInjector.nextDouble() < 0.7) {
            throw new IllegalStateException("Lỗi dịch vụ tạm thời");
        }
        return "Dịch vụ hoạt động bình thường";
    }

    // Xử lý khi bị chặn do vượt giới hạn
    public String onRequestBlocked(BlockException ex) {
        return "❌ Đã đạt ngưỡng giới hạn lưu lượng. Vui lòng thử lại sau.";
    }

    // Xử lý khi người dùng cụ thể bị giới hạn
    public String onUserRateLimited(Long id, BlockException ex) {
        return "⏳ Người dùng " + id + " đang bị tạm dừng do quá tải.";
    }

    // Xử lý khi mạch bị ngắt
    public String onCircuitOpen(BlockException ex) {
        return "🔌 Dịch vụ đang tạm ngừng để bảo trì.";
    }

    // Fallback khi có lỗi nghiệp vụ
    public String serviceFallback(Throwable t) {
        return "🔄 Đang sử dụng chế độ dự phòng: " + t.getMessage();
    }
}

Giải Thích Cơ Chế Hoạt Động

Tài Nguyên và Gắn Nhãn

Annotation @SentinelResource đánh dấu một đoạn mã cần được bảo vệ. Giá trị value là định danh duy nhất dùng để áp dụng quy tắc.

Phân Biệt Xử Lý Lỗi

  • BlockHandler: Xử lý các trường hợp bị từ chối bởi Sentinel (vượt QPS, ngắt mạch...). Nhận tham số bổ sung là BlockException.
  • Fallback: Xử lý lỗi phát sinh trong logic nghiệp vụ (ngoại lệ thông thường). Nhận tham số Throwable.

Kiểm Thử Thực Tế

  1. Thử nghiệm giới hạn QPS: Gọi nhanh /api/simple hơn 3 lần/giây → nhận phản hồi chặn.
  2. Thử nghiệm theo người dùng: Gọi /api/user/123 liên tục → bị chặn sau lần thứ hai.
  3. Thử nghiệm ngắt mạch: Gọi /api/faulty khoảng 6-8 lần → mạch sẽ ngắt trong 15 giây.

Nguyên Tắc Triển Khai Tốt Nhất

  • Đặt tên tài nguyên rõ ràng: Dùng định dạng mô tả như serviceName#operation.
  • Không xử lý nặng trong handler: BlockHandler và Fallback nên trả về kết quả nhanh, tránh I/O hay tính toán phức tạp.
  • Dùng Console để cấu hình động: Trên môi trường production, nên dùng Sentinel Dashboard thay vì hard-code rule.
  • Giám sát và cảnh báo: Tích hợp với hệ thống metrics (Prometheus, Grafana) để theo dõi trạng thái tài nguyên.
  • Phân tầng bảo vệ: Áp dụng cả giới hạn lưu lượng và ngắt mạch cho các dịch vụ quan trọng.

Thẻ: sentinel spring-boot rate-limiting circuit-breaker resilience

Đăng vào ngày 1 tháng 6 lúc 01:20