Kỹ Sư Thực Chiến - Tư Duy Lường Trước Rủi Ro Hệ Thống Tập 3: "Sự Sụp Đổ Dây Chuyền" (Tư duy cô lập lỗi - Fallback & Circuit Breaker)
Khi làm một hệ thống phân tán, người mới thường có niềm tin ngây thơ rằng: "Chia nhỏ các service ra, thằng nào chết thì chỉ thằng đó chịu, hệ thống vẫn sống". Nhưng thực tế, trong một mớ bòng bong các API gọi chéo lẫn nhau, một service đối tác (hoặc nội bộ) bị chậm có thể kéo sập toàn bộ các service khác như hiệu ứng Domino.
Dưới đây là cách một Kỹ sư kinh nghiệm thiết lập các "vách ngăn chống cháy" để bảo vệ hệ thống.
1. Hiệu Ứng Domino & Cạn Kiệt Tài Nguyên (Resource Exhaustion)
- Góc nhìn của Senior: Lỗi chết hẳn (Crash / HTTP 500) đôi khi lại dễ xử lý hơn là lỗi "treo" (Timeout).
- Kịch bản thực tế: Giả sử Central Server của trạm vé gọi API sang cổng thanh toán của ngân hàng để nạp tiền. Bình thường mất 200ms. Bỗng một ngày, ngân hàng bị lag, phản hồi mất 10 giây.
- Hậu quả nếu code thiếu phòng thủ: Các Goroutine hoặc thread chịu trách nhiệm gọi API sẽ bị kẹt lại chờ 10 giây. Traffic vẫn đổ vào liên tục, hệ thống sinh thêm hàng ngàn Goroutine mới để xử lý, cho đến khi cạn kiệt toàn bộ RAM và CPU. Server trạm vé sập, kéo theo cả hệ thống soát vé tê liệt chỉ vì... ngân hàng lag.
2. Circuit Breaker (Cầu Dao Ngắt Mạch) – Cắt Lỗ Tránh Thảm Họa
Để chặn đứng hiệu ứng Domino, hệ thống phải được gắn các "aptomat". Khi phát hiện dòng điện có vấn đề, cầu dao phải tự động ngắt.
Cơ chế hoạt động (3 trạng thái cốt lõi):
- Closed (Đóng mạch): Hệ thống bình thường, request đi qua trót lọt.
- Open (Ngắt mạch): Khi tỷ lệ lỗi (hoặc timeout) vượt quá ngưỡng cho phép (VD: 50% request xịt trong 10 giây), Circuit Breaker chuyển sang Open. Lúc này, mọi request gọi sang service đó sẽ bị từ chối ngay lập tức (Fast Failure) mà không cần phải chờ kết nối mạng. Tài nguyên được giải phóng ngay.
- Half-Open (Chờ khôi phục): Sau một khoảng thời gian (VD: 30 giây), cầu dao hé mở lại một chút, cho phép một lượng nhỏ request đi qua để "thăm dò". Nếu thành công, nó đóng lại (Closed). Nếu vẫn tịt, nó lại ngắt (Open).
Tư duy áp dụng: Không bao giờ để các lời gọi mạng (network calls) giữa các hệ thống (cả nội bộ lẫn bên ngoài) chạy mà không đi qua Circuit Breaker.
3. Fallback (Lối Cấp Cứu) – Nghệ Thuật Ứng Biến
Khi Circuit Breaker ngắt (Open), request trả về lỗi. Nhưng trả lỗi thế nào để User không chửi bới, đó là nhiệm vụ của Fallback.
- Góc nhìn của Senior: Đừng để người dùng thấy một trang trắng bóc hoặc mã lỗi 503 Service Unavailable. Phải có kịch bản dự phòng.
Các chiến thuật Fallback phổ biến:
- Trả về dữ liệu cũ (Stale Data): Đang truy vấn danh sách lịch sử giao dịch mà Service quản lý dữ liệu lỗi? Fallback về Redis để lấy bản cache lưu từ 1 tiếng trước, dĩ nhiên kèm theo dòng thông báo nhẹ nhàng: "Dữ liệu có thể chưa được cập nhật mới nhất".
- Trả về giá trị mặc định (Default Value): Service gợi ý phim đang hot bị chết? Fallback trả về danh sách 5 bộ phim mặc định cố định trong code.
- Chuyển sang Async (Bất đồng bộ): Cổng thanh toán chết không thể trừ tiền ngay? Fallback ghi nhận giao dịch vào Message Queue để chờ xử lý ngầm (Store & Forward), báo khách hàng "Giao dịch đang được xử lý, chúng tôi sẽ cập nhật số dư sau vài phút".
4. Nghệ Thuật Retry (Thử Lại) & Nỗi Đau "Thundering Herd"
Khi gọi API lỗi, phản xạ tự nhiên của Fresher là viết một vòng lặp for để Retry 3 lần liên tiếp. Đây là hành động "tự DDoS" chính hệ thống của mình.
- Kịch bản nguy hiểm: Cổng thanh toán vừa bị quá tải sập nguồn. Vừa ngoi lên lại được, ngay lập tức hệ thống của bạn (cùng hàng loạt đối tác khác) tấp hàng triệu cái request Retry vào cùng 1 tích tắc (hiện tượng Thundering Herd - Hiệu ứng bầy đàn). Cổng thanh toán lại sập tiếp.
Thiết kế Retry chuẩn kỹ sư:
- Exponential Backoff (Lùi bước theo hàm mũ): Không retry liên tục mỗi giây. Lần 1 chờ 1s, lần 2 chờ 2s, lần 3 chờ 4s, lần 4 chờ 8s. Giúp hệ thống đối tác có thời gian thở.
- Jitter (Độ nhiễu): Thêm một chút thời gian ngẫu nhiên (random milliseconds) vào mỗi lần retry (VD: thay vì đúng 2s, thì là 2.1s hoặc 1.8s) để các request không đập vào server cùng một phần nghìn giây.
- Quy tắc vàng: Chỉ Retry các lỗi liên quan đến đường truyền (Network Error, HTTP 503, 504) hoặc các API Idempotent (như
GET). Tuyệt đối không tự động Retry một lệnhPOSTtạo giao dịch trừ tiền mà không có khóa an toàn.
All rights reserved