
Circuit Breaker Pattern là một mẫu thiết kế lập trình giúp hệ thống chịu lỗi tốt hơn bằng cách ngắt kết nối tới dịch vụ đang hỏng, thay vì cứ gọi đi gọi lại và kéo theo cả hệ thống sụp xuống. Trong các hệ thống phân tán, đây là lớp phòng thủ cơ bản chống lại hiệu ứng domino khi một dịch vụ phụ trở nên quá tải. Bài viết này giải thích máy trạng thái của circuit breaker, cách cấu hình ngưỡng, và vì sao nên kết hợp nó với retry, timeout và fallback.

Vì sao cần circuit breaker
Hãy tưởng tượng một dịch vụ tra cứu địa chỉ nhận hàng nghìn yêu cầu mỗi giây. Khi nó bị quá tải, mỗi yêu cầu vẫn được gửi đi, vẫn chờ hết thời gian chờ, rồi mới trả về lỗi. Trong khoảng thời gian chờ đó, các luồng xử lý (thread) của dịch vụ gọi bị chiếm dùng, và bản thân dịch vụ gọi cũng đang sống dựa vào các dịch vụ khác. Kết quả là tài nguyên bị bóp chết, độ trễ tăng vọt, và toàn bộ chuỗi dịch vụ cùng chậm lại.
Đây chính là hiệu ứng lỗi dây chuyền (cascading failure). Circuit breaker chèn vào giữa hai bên để cắt đứt chuỗi này: khi tỉ lệ lỗi vượt ngưỡng, breaker ngay lập tức từ chối các lời gọi mới mà không cần chờ mạng phản hồi. Hệ thống có thời gian để bình phục, còn người dùng nhận thông báo lỗi tức thì thay vì treo.
Khác biệt then chốt so với cách chỉ đặt timeout: timeout vẫn tiêu tốn tài nguyên trong suốt thời gian chờ, và những lời gọi tới dịch vụ khoẻ vẫn có thể bị chặn nhầm khi mạng chậm. Circuit breaker chủ động phát hiện dịch vụ không phản hồi và ngăn lặp lại những lần thử vô ích.

Ba trạng thái của circuit breaker
Circuit breaker vận hành qua ba trạng thái chuyển tiếp lẫn nhau:
1. Closed — đang bình thường
Đây là trạng thái mặc định khi khởi tạo. Mọi lời gọi đều đi thẳng tới dịch vụ đích. Đồng thời, breaker ghi nhận kết quả thành công hoặc thất bại. Khi tỉ lệ lỗi vượt ngưỡng cấu hình, nó chuyển sang trạng thái Open.
2. Open — đang ngắt
Ở trạng thái này, breaker từ chối ngay lập tức mọi lời gọi mà không đụng tới mạng. Sau một khoảng thời gian chờ (thường 30 đến 60 giây), nó chuyển sang Half-Open để thử lại.
3. Half-Open — đang thử
Breaker cho phép một lượng nhỏ lời gọi thử nghiệm đi qua. Nếu chúng thành công, breaker đóng lại và trở về trạng thái Closed. Nếu có bất kỳ lời gọi nào thất bại, breaker quay lại Open và bắt đầu chu kỳ chờ mới.
| Trạng thái | Lời gọi tới dịch vụ đích | Khi nào chuyển trạng thái |
|---|---|---|
| Closed | Cho phép toàn bộ | Tỉ lệ lỗi vượt ngưỡng |
| Open | Chặn ngay, trả lỗi tức thì | Hết thời gian chờ |
| Half-Open | Cho phép số lượng nhỏ thử nghiệm | Thành công thì Closed, thất bại thì Open |
Cấu hình các tham số quan trọng
Một circuit breaker hiệu quả cần bốn tham số được cân bằng, và bảng dưới đây tóm tắt vai trò từng tham số cùng giá trị khởi điểm thường dùng:
| Tham số | Ý nghĩa | Gợi ý ban đầu |
|---|---|---|
| Ngưỡng tỉ lệ lỗi | Tỉ lệ thất bại kích hoạt ngắt | 50% trong cửa sổ 10 giây |
| Số lời gọi tối thiểu | Ngưỡng mẫu tối thiểu kích hoạt | 20 lời gọi |
| Thời gian chờ mở | Bao lâu thì thử lại | 30 giây |
| Lời gọi thử ở Half-Open | Số lời gọi thử nghiệm | 3 lời gọi |
Điểm dễ sai nhất là số lời gọi tối thiểu. Nếu không có ngưỡng này, chỉ cần một lời gọi lỗi duy nhất trong một khoảng thời gian dài cũng đủ để ngắt cả dịch vụ — điều không mong muốn. Ngưỡng mẫu tối thiểu bảo đảm quyết định ngắt chỉ được đưa ra khi có đủ dữ liệu thống kê đáng tin cậy.
Với dịch vụ quan trọng, hãy cân nhắc áp dụng circuit breaker riêng cho từng thao tác thay vì toàn cục. Ví dụ, đọc danh mục sản phẩm và ghi đơn hàng có thể có mức chịu lỗi khác nhau; ngắt cả hai chỉ vì một cái chậm là quá tay.
Kết hợp với retry, timeout và fallback
Circuit breaker không hoạt động tốt nếu đứng một mình. Mẫu thiết kế này sinh ra để đi kèm với ba kỹ thuật khác, mỗi kỹ thuật xử lý một mặt của bài toán:
- Timeout đặt trần thời gian chờ cho mỗi lời gọi, đảm bảo một lời gọo treo không giữ chân luồng xử lý vô thời hạn.
- Retry dùng cho lỗi tạm thời, nhưng phải giới hạn số lần và chỉ áp dụng cho lỗi có tính chất tức thời. Retry không giới hạn cộng với circuit breaker đang đóng là nguyên nhân phổ biến làm tăng tải kép.
- Fallback cung cấp câu trả lời thay thế khi dịch vụ chính không khả dụng: trả dữ liệu cache cũ, dùng bản sao dự phòng, hoặc hiển thị thông báo lỗi thân thiện hơn.
Thứ tự kiểm tra cũng quan trọng. Timeout nên được áp dụng bên trong breaker, để thời gian chờ được tính vào bộ đếm thành công/thất bại. Còn retry nên đặt phía ngoài, để một lần thử lại vẫn bị breaker quan sát và tính vào thống kê.
Nhiều thư viện phổ biến đã cài sẵn circuit breaker cùng khả năng đo lường, chẳng hạn Resilience4j cho Java, Polly cho .NET, hay thư viện circuitbreaker trong hệ sinh thái Python. Nếu dự án đã dùng thư viện tương tự với retry, hãy dùng luôn thư viện đó thay vì tự viết lại máy trạng thái.
Lưu ý khi triển khai thật
Circuit breaker nghe thì đơn giản, nhưng triển khai sai khá dễ gây hiệu ứng ngược lại. Một số lưu ý thực tế:
- Đặt breaker ở ranh giới mỗi lời gọi mạng, không đặt ở cấp ứng dụng toàn cục — nếu không, một lỗi cục bộ cũng làm tắt cả dịch vụ.
- Luôn kết hợp với giới hạn tốc độ (rate limiting) để tránh tình huống hàng nghìn client cùng gọi lại trong khoảng thời gian chờ.
- Giám sát số lần breaker mở như một tín hiệu cảnh báo. Một hệ thống khỏe mạnh gần như không bao giờ ngắt; nếu ngắt thường xuyên, vấn đề nằm ở dịch vụ đích chứ không phải ở breaker.
- Chọn hành vi trả về khi bị chặn. Trả lỗi rõ ràng giúp việc gỡ lỗi dễ hơn so với việc âm thầm nuốt yêu cầu.
Tóm lại, circuit breaker là một mẫu thiết kế nhỏ gọn nhưng đóng vai trò quan trọng trong kiến trúc phân tán. Nó biến một lỗi cục bộ thành sự cố chứ không phải thảm hoạ, đồng thời giữ cho hệ thống đủ thông minh để tự thử lại khi dịch vụ phụ hồi. Chỉ cần nhớ rằng breaker chỉ là một mảnh ghép: hiệu quả nhất khi nó đi cùng timeout, retry giới hạn, fallback và giám sát phù hợp.
