Circuit Breaker Pattern — đừng để một service chết kéo sập cả hệ thống
Hồi mới ra trường đi làm, mình từng chứng kiến một vụ sập hệ thống kinh điển: service A gọi service B, B gọi C, C gọi D. Đùng một cái, D chết vì quá tải — và chỉ trong vài phút, A, B, C cũng chết theo vì hết connection pool. Cả hệ thống ngưng hoạt động chỉ vì một service nhỏ nhất ngỏm.
Lúc đó mình chỉ biết restart cả đám và cầu trời nó đừng tái diễn. Nhưng về sau mình mới biết có một pattern xử lý chuyện này rất gọn: Circuit Breaker.
Ảnh: ranjeet . — Pexels
Circuit Breaker là gì?
Nói nôm na, Circuit Breaker là một lớp trung gian giữa client và service. Nó theo dõi số lần request thất bại. Khi lỗi vượt ngưỡng, nó ngắt mạch — không cho request nào đi tới service đang chết nữa, cho service đó thời gian hồi phục.
Ba trạng thái cơ bản:
- Closed (đóng): Mọi request đi qua bình thường. Circuit breaker đếm lỗi.
- Open (ngắt): Khi số lỗi vượt ngưỡng. Request bị từ chối ngay lập tức (fail fast).
- Half-Open (nửa mở): Sau một thời gian, cho một request thử đi qua. Nếu thành công → đóng lại. Nếu thất bại → mở tiếp.
Code mẫu trong Go
Mình viết một circuit breaker đơn giản để anh em dễ hình dung:
package circuitbreaker
import (
"sync"
"time"
)
type State int
const (
StateClosed State = iota
StateOpen
StateHalfOpen
)
type CircuitBreaker struct {
mu sync.RWMutex
state State
failureCount int
threshold int
resetTimeout time.Duration
lastFailureTime time.Time
}
func New(threshold int, timeout time.Duration) *CircuitBreaker {
return &CircuitBreaker{
state: StateClosed,
threshold: threshold,
resetTimeout: timeout,
}
}
func (cb *CircuitBreaker) Call(fn func() error) error {
cb.mu.Lock()
if cb.state == StateOpen {
if time.Since(cb.lastFailureTime) > cb.resetTimeout {
cb.state = StateHalfOpen
} else {
cb.mu.Unlock()
return fmt.Errorf("circuit breaker is open")
}
}
cb.mu.Unlock()
err := fn()
cb.mu.Lock()
defer cb.mu.Unlock()
if err != nil {
cb.failureCount++
cb.lastFailureTime = time.Now()
if cb.failureCount >= cb.threshold {
cb.state = StateOpen
}
} else {
if cb.state == StateHalfOpen {
cb.state = StateClosed
cb.failureCount = 0
}
}
return err
}
Đơn giản vậy thôi. Dùng nó:
cb := circuitbreaker.New(5, 30*time.Second)
err := cb.Call(func() error {
resp, err := http.Get("http://dependency-service/api/data")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 500 {
return fmt.Errorf("server error: %d", resp.StatusCode)
}
return nil
})
Ảnh: Rashed Paykary — Pexels
Kinh nghiệm thực tế
Mình đã deploy circuit breaker cho một hệ thống xử lý payment. Hồi đó mỗi lần payment gateway chậm, cả cụm service khác cũng ngừng theo vì hết goroutines, hết connections. Sau khi thêm circuit breaker:
- Payment gateway timeout 30s → fail fast sau 3 lần thất bại liên tiếp
- Các service khác không bị ảnh hưởng
- Circuit breaker tự động reset sau 1 phút, payment gateway có thời gian hồi phục
- Giảm ~70% cảnh báo lỗi liên quan tới timeout
Có một chuyện mình cũng rút ra: chọn threshold và reset timeout sao cho phù hợp. Nếu threshold quá thấp (2-3 lần), chỉ một lỗi nhỏ cũng mở breaker. Nếu quá cao (100+ lần), thì không khác gì không có.
Thường mình dùng:
- Threshold: 5-10 lần cho internal service, cao hơn cho external API
- Reset timeout: 10-30 giây cho internal, 30-60 giây cho external
- Kết hợp với retry có exponential backoff nữa thì càng ngon
Khi nào không nên dùng?
Circuit breaker không phải cái gì cũng gắn vào được. Mấy trường hợp không nên dùng:
- Local function call — không có network, lỗi là do code, không cần breaker
- Idempotent operations — nếu retry không gây hại, breaker chỉ làm chậm thêm
- Critical path quá nhạy cảm — như health check endpoint, không nên bị block bởi breaker
Ảnh: Mikhail Nilov — Pexels
Tổng kết
Circuit Breaker là một pattern đơn giản nhưng cực kỳ hiệu quả cho microservices. Nó giúp hệ thống của bạn fail gracefully thay vì sập lan truyền. Kết hợp với retry, timeout, và bulkhead pattern nữa thì bạn có một hệ thống siêu chịu chơi.
Nếu bạn chưa dùng nó, hãy thử trong project tới. Thư viện như gobreaker (Go), resilience4j (Java), pybreaker (Python) có sẵn rồi — chỉ việc xài thôi.
📋 Phụ lục thuật ngữ
- Circuit Breaker — pattern ngăn chặn request đến service đang lỗi, giúp hệ thống hồi phục trước khi gửi request lại
- Cascade failure — lỗi lan truyền từ service này sang service khác
- Fail fast — từ chối request ngay lập tức khi biết chắc sẽ thất bại
- Threshold — ngưỡng số lần lỗi để mở circuit breaker
- Reset timeout — thời gian chờ trước khi thử lại request