2 giờ sáng. Production alert. Payment Service timeout — cascade xuống Order Service, Inventory Service, Notification Service. Toàn bộ checkout luồng sập. Bạn nhìn vào dashboard: CPU bình thường, DB bình thường. Vậy tại sao?
Lý do thật sự: không có circuit breaker, không có saga pattern, không có distributed tracing. Bài này giải thích 5 thứ developer microservices hay bỏ qua — và cách chúng ngăn hệ thống sập lúc 2 giờ sáng.
Sau khi áp dụng circuit breaker + structured logging tại một fintech team, MTTR (mean time to recovery) giảm từ 45 phút xuống còn 8 phút — không phải vì code tốt hơn, mà vì họ nhìn thấy được chuyện gì đang xảy ra.
1. Circuit Breaker — Tại Sao Một Service Làm Sập Cả Hệ Thống
Trong microservices, services gọi nhau qua network. Khi Payment Service chậm (timeout 30s), mỗi request vào Order Service đều bị block 30 giây chờ Payment. Thread pool cạn kiệt. Order Service không nhận được request mới. Cascade tiếp tục.
Circuit Breaker hoạt động như cầu dao điện: khi lỗi vượt ngưỡng, nó “ngắt” ngay thay vì tiếp tục thử — trả lỗi ngay lập tức thay vì chờ timeout.
Ba trạng thái của Circuit Breaker
- Closed — Hoạt động bình thường, requests đi qua. Đếm lỗi.
- Open — Lỗi vượt ngưỡng. Tất cả requests bị reject ngay, không gọi downstream.
- Half-Open — Sau cooldown period, cho 1 request thử. Nếu thành công → về Closed. Nếu thất bại → về Open.
Ví dụ với Resilience4j (Java):
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofSeconds(30))
.slidingWindowSize(10)
.build();
CircuitBreaker cb = CircuitBreakerRegistry.of(config)
.circuitBreaker("paymentService");
Supplier<Payment> decorated = CircuitBreaker.decorateSupplier(cb,
() -> paymentService.charge(order));
try {
Payment result = decorated.get();
} catch (CallNotPermittedException e) {
return fallbackResponse();
}
Điểm quan trọng: Luôn implement fallback. Khi circuit open, trả về cached data, default response, hoặc error message rõ ràng — đừng để user nhìn thấy NullPointerException.
2. Distributed Transactions — 2PC Thất Bại Ở Đâu Và Tại Sao Saga Là Giải Pháp
Trong monolith, transaction đơn giản: BEGIN; UPDATE orders; UPDATE inventory; COMMIT;. Nếu lỗi ở bước 2, rollback toàn bộ.
Trong microservices, mỗi service có database riêng. Không có shared transaction. Vấn đề thực tế: bạn đã charge credit card nhưng Order Service sập trước khi tạo order — tiền mất nhưng đơn hàng không tồn tại.
Tại Sao 2PC Không Hoạt Động Trong Microservices
- Blocking protocol: Nếu coordinator sập giữa chừng, tất cả participants bị lock vô thời hạn
- Single point of failure: Coordinator down = toàn bộ hệ thống chờ
- Performance: 2 round trips mạng cho mỗi transaction — không scale được
Saga Pattern — Choreography vs Orchestration
Saga chia transaction thành chuỗi local transactions. Mỗi bước thành công emit event. Nếu bước nào đó thất bại, chạy compensating transaction để undo các bước trước.
Choreography — Services tự lắng nghe events và react:
// Order Service
orderCreated → publish "OrderPlaced" event
// Payment Service (lắng nghe "OrderPlaced")
chargeCard()
→ success: publish "PaymentCompleted"
→ failure: publish "PaymentFailed"
// Inventory Service (lắng nghe "PaymentCompleted")
reserveItems()
→ success: publish "InventoryReserved"
→ failure: publish "InventoryFailed" → trigger compensation
// Compensation: Payment Service lắng nghe "InventoryFailed" → refund card
Orchestration — Saga Orchestrator điều phối toàn bộ flow:
class OrderSagaOrchestrator:
def execute(order_id):
payment_result = payment_service.charge(order_id)
if payment_result.failed:
return saga_failed("payment")
inventory_result = inventory_service.reserve(order_id)
if inventory_result.failed:
payment_service.refund(order_id) # compensate
return saga_failed("inventory")
shipping_service.schedule(order_id)
return saga_success()
Khi nào dùng gì? Choreography tốt cho flow đơn giản, ít steps. Orchestration tốt khi flow phức tạp, cần visibility rõ ràng về state của saga.
3. Sync vs Async — Khi Nào Nên Dùng Message Queue
Temporal coupling là vấn đề cốt lõi của synchronous calls: Service A chỉ hoạt động được khi Service B đang chạy. Nếu B down, A cũng down theo — dù A không cần response ngay.
Ví dụ thực tế: Sau khi user đặt order thành công, hệ thống cần gửi email xác nhận, cập nhật analytics, và notify warehouse. Không có cái nào cần response ngay. Nhưng nếu gọi sync, Order Service phải chờ cả 3 services trả lời mới hoàn thành request.
Tail latency amplification: Nếu mỗi service có P99 = 100ms, và bạn gọi 5 services sync, P99 tổng thể ≈ 400ms (không phải 100ms). Latency cộng dồn theo số lượng calls.
Rule of Thumb
- Sync (HTTP/gRPC): Khi cần kết quả ngay để tiếp tục xử lý (payment result, auth check, inventory availability)
- Async (Message Queue): Khi không cần kết quả ngay (email, analytics, notifications, audit logs)
- Async cũng có trade-off: Eventual consistency — data có thể chưa consistent ngay sau write. User có thể thấy stale data trong vài giây.
4. Observability — Làm Sao Nhìn Thấy Hệ Thống Đang Làm Gì
Logging trong monolith: grep file log, tìm error. Trong microservices: một request đi qua 8 services, mỗi service có log riêng, trên server riêng. Làm sao trace request đó?
Distributed Tracing
Gắn trace-id duy nhất vào mỗi request ngay từ entry point (API Gateway). Tất cả services đều propagate trace-id này trong header:
// HTTP Header
X-Trace-ID: 550e8400-e29b-41d4-a716-446655440000
X-Span-ID: 7d4c8a2b-f3e1-4a9c-b8d7-2e5f9a1c3b4e
// Mỗi service log kèm trace-id
{
"timestamp": "2026-05-11T02:00:00Z",
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"span_id": "7d4c8a2b-...",
"service": "payment-service",
"action": "charge_card",
"duration_ms": 245,
"status": "success"
}
Với OpenTelemetry, bạn có thể visualize toàn bộ request path: từ API Gateway vào Order Service → Payment Service → Notification Service, thấy rõ bottleneck ở đâu.
Structured Logging
Đừng log string thuần: "Payment failed for user 123". Log JSON có cấu trúc để query được:
// ❌ String log — không query được
logger.error("Payment failed for user " + userId + " amount " + amount);
// ✅ Structured log — query được bằng Kibana/Grafana
logger.error({
event: "payment_failed",
user_id: userId,
amount: amount,
currency: "VND",
error_code: "INSUFFICIENT_FUNDS",
trace_id: context.traceId
});
RED Method — 3 Metrics Cần Theo Dõi
- R — Rate: Số requests/second mỗi service đang xử lý
- E — Errors: Tỉ lệ lỗi (%) — alert khi vượt ngưỡng
- D — Duration: Latency phân vị P50/P95/P99 — P99 mới là số quan trọng nhất
P99 = 500ms có nghĩa là 1% requests chậm hơn 500ms. Với 1 triệu requests/ngày, đó là 10,000 users bị ảnh hưởng.
5. Những Lỗi Thường Gặp Sau 6–12 Tháng Dùng Microservices
Kitty Problem — Shared Data Model
Team chia services nhưng share chung một User model. Order Service, Payment Service, Notification Service đều import com.company.shared.model.User. Kết quả: thay đổi User model phải deploy lại tất cả services cùng lúc — đây không phải microservices, đây là distributed monolith.
Fix: Mỗi service có data model riêng, chỉ chứa fields nó cần. Order Service chỉ cần user_id và shipping_address — không cần biết user.preferences.theme.
Idempotency Trong Message Consumers
Message queue đảm bảo “at-least-once delivery” — nghĩa là message có thể được gửi nhiều lần. Nếu Payment Service nhận “OrderPlaced” event 2 lần mà không check idempotency, user bị charge 2 lần.
Fix: Lưu event_id vào database sau khi xử lý. Trước khi process, check xem event này đã được xử lý chưa:
async function handleOrderPlaced(event) {
const alreadyProcessed = await db.events.findOne({ event_id: event.id });
if (alreadyProcessed) return; // idempotency check
await chargeCard(event.order_id);
await db.events.insert({ event_id: event.id, processed_at: new Date() });
}
Graceful Shutdown
Khi Kubernetes kill một pod để deploy version mới, pod nhận SIGTERM. Nếu service không handle SIGTERM, nó bị kill ngay — những requests đang xử lý bị drop, message queue consumers mất message.
Fix: Handle SIGTERM, ngừng nhận requests mới, chờ requests hiện tại hoàn thành (thường 30s), sau đó mới shutdown.
Kết Luận
Microservices không tự nhiên reliable. Reliability là thứ bạn phải build vào hệ thống — từng layer một:
- ✅ Circuit Breaker: ngăn cascade failure
- ✅ Saga Pattern: distributed transactions không cần 2PC
- ✅ Async khi có thể: giảm temporal coupling
- ✅ Observability: distributed tracing + structured logging + RED metrics
- ✅ Idempotency: at-least-once delivery không gây double-processing
Nếu bạn đang bắt đầu với microservices, đọc thêm bài trước về Microservices Architecture cơ bản và Service Boundary design — hai thứ đó là foundation trước khi lo đến circuit breaker.
Để đi sâu hơn: Resilience4j Circuit Breaker documentation và Patterns of Distributed Systems — Martin Fowler là hai tài liệu bắt buộc đọc.


Gửi phản hồi