Fault Tolerance
Xem thửCổng thanh toán chậm, và cả website chậm theo
Một chiều thứ Sáu, nhà cung cấp thanh toán mà gdev-shop dùng bắt đầu trả lời chậm: thay vì 200 ms, mỗi request mất 30 giây rồi mới hết hạn. Mỗi request checkout giữ một luồng xử lý của server trong 30 giây. Chỉ vài phút sau, toàn bộ luồng xử lý của mọi server bị giữ, trang chủ, tìm kiếm, xem sản phẩm (không liên quan gì đến thanh toán) cũng ngừng phản hồi. Một phụ thuộc chậm đã kéo cả hệ thống xuống. Hiện tượng này gọi là lỗi dây chuyền (cascading failure).
Fault tolerance (khả năng chịu lỗi) là tập hợp các kỹ thuật để một sự cố ở một nơi không lan ra làm hỏng nơi khác.
Fault tolerance là gì?
Fault tolerance là khả năng của hệ thống tiếp tục hoạt động, có thể ở mức giảm chức năng, khi một hoặc nhiều thành phần gặp lỗi, thay vì sụp đổ hoàn toàn.
Nó nằm trong họ các khái niệm bạn đã gặp, và cần phân biệt:
| Khái niệm | Trọng tâm | Câu hỏi |
|---|---|---|
| Availability | Tỷ lệ thời gian phục vụ được | Hệ thống có đang chạy không? |
| High Availability | Thiết kế dự phòng để đạt availability cao | Thành phần hỏng thì có bản thay thế không? |
| Reliability | Làm đúng theo thời gian | Hệ thống có làm đúng và đáng tin không? |
| Fault tolerance | Hành vi khi có lỗi: chịu, cô lập, giảm cấp | Khi lỗi xảy ra, nó có lan ra và làm sập tất cả không? |
Fault tolerance là phương tiện góp phần vào cả HA và reliability. Điểm riêng của nó: tập trung vào cách hệ thống phản ứng với lỗi đang xảy ra, đặc biệt các lỗi một phần (partial failure: một phụ thuộc chậm hoặc lỗi trong khi phần còn lại ổn).
Các kỹ thuật cốt lõi
- Timeout: không chờ vô hạn. Gọi cổng thanh toán tối đa 2 giây rồi bỏ cuộc. Nguyên tắc: mọi lời gọi qua mạng phải có timeout.
- Retry có kiểm soát: thử lại lỗi tạm thời với giãn cách tăng dần và giới hạn số lần, chỉ với thao tác idempotent.
- Circuit breaker (cầu dao): nếu một phụ thuộc lỗi liên tục, ngừng gọi nó một lúc thay vì tiếp tục làm hỏng bản thân. Giải thích chi tiết bên dưới.
- Bulkhead (vách ngăn): tách tài nguyên theo phụ thuộc để lỗi một nơi không chiếm hết tài nguyên chung. Ví dụ dành riêng 20 luồng cho thanh toán, 100 luồng còn lại cho mọi việc khác.
- Graceful degradation (hạ cấp có kiểm soát): giữ các chức năng quan trọng khi một phần hỏng. Ví dụ thanh toán lỗi thì vẫn cho đặt hàng ở trạng thái chờ.
- Load shedding (xả tải): khi quá tải, từ chối bớt các yêu cầu ít quan trọng để bảo vệ các yêu cầu quan trọng, thay vì chậm đồng đều rồi sập.
Circuit breaker hoạt động ra sao
Đang tải Animation…
Đang tải sơ đồ…
Đang tải Animation…
Cách đọc sơ đồ
Circuit breaker giống cầu dao điện, có ba trạng thái:
- Closed (đóng, bình thường): các lời gọi đi qua phụ thuộc như thường, và breaker đếm lỗi. Khi tỷ lệ lỗi hoặc số lần timeout vượt ngưỡng (ví dụ 50% trong 10 giây), nó chuyển sang
Open. - Open (mở, ngắt mạch): breaker chặn ngay mọi lời gọi tới phụ thuộc và trả lỗi hoặc phương án dự phòng tức thì, không chờ. Nhờ đó luồng xử lý không bị giữ, và phụ thuộc đang yếu có thời gian hồi phục thay vì bị dồn thêm.
- HalfOpen (nửa mở, thử nghiệm): sau một khoảng chờ, breaker cho một số ít lời gọi thử đi qua. Nếu thành công, nó đóng lại (
Closed). Nếu thất bại, nó mở lại (Open) và chờ tiếp.
Lợi ích cốt lõi: biến "chậm 30 giây mỗi request" thành "thất bại tức thì", và bảo vệ tài nguyên của chính bạn. Cái giá: bạn cần quyết định làm gì khi mạch mở (xem bên dưới), và ngưỡng cần chỉnh cẩn thận: quá nhạy thì ngắt không cần thiết, quá trễ thì không bảo vệ kịp.
Áp dụng cho gdev-shop
Đang tải sơ đồ…
Đang tải Animation…
Cách đọc sơ đồ
- Request checkout đến. Hệ thống kiểm tra circuit breaker của thanh toán đang mở hay đóng.
- Mạch đóng: gọi nhà cung cấp thanh toán với timeout 2 giây. Thành công thì đơn thành
PAID. - Mạch mở, hoặc lời gọi timeout hay lỗi: đơn được lưu ở trạng thái
PENDING_PAYMENTthay vì trả lỗi cho khách, và một worker thử thanh toán lại sau khi nhà cung cấp hồi phục.
Đây là hạ cấp có kiểm soát kết hợp circuit breaker: người dùng vẫn đặt được hàng, trang chủ và tìm kiếm không hề bị ảnh hưởng, vì luồng xử lý không bị giữ 30 giây. Kết quả khác hẳn kịch bản đầu bài.
Bottleneck cần để ý: hàng đợi các đơn PENDING_PAYMENT có thể dồn lại khi sự cố kéo dài, và khi nhà cung cấp hồi phục, worker dồn dập thử lại có thể quá tải nó lần nữa. Cần giới hạn tốc độ thử lại. Rủi ro khác: khách có thể hoang mang với trạng thái chờ, nên giao diện cần thông báo rõ.
Chi phí của fault tolerance
- Độ phức tạp: nhiều cơ chế phải cấu hình, kiểm thử và hiểu.
- Phải thiết kế phương án khi lỗi: breaker mở thì làm gì là quyết định nghiệp vụ, không phải kỹ thuật thuần túy.
- Cấu hình sai tạo vấn đề mới: timeout quá ngắn làm lỗi giả, ngưỡng breaker quá nhạy làm ngắt mạch không đáng.
- Phải kiểm thử bằng cách gây lỗi thật trong môi trường kiểm soát, vì cơ chế chưa từng chạy thật thường không hoạt động như kỳ vọng.
Những hiểu lầm phổ biến
- "Fault tolerance nghĩa là không bao giờ có lỗi." Nó chấp nhận lỗi xảy ra và giới hạn tác động.
- "Cứ retry là chịu lỗi." Retry thiếu kiểm soát làm phụ thuộc đang yếu sập hẳn. Cần timeout, backoff, giới hạn và circuit breaker.
- "Circuit breaker tự động giải quyết mọi thứ." Nó chỉ giúp ngừng gọi phụ thuộc hỏng. Bạn vẫn phải có phương án khi mạch mở.
- "Fault tolerance và HA là một." HA tập trung dự phòng để duy trì phục vụ, fault tolerance tập trung cách cô lập và xử lý lỗi, gồm cả lỗi một phần.
Must Know, Good to Know, Advanced
- Must Know: lỗi dây chuyền; timeout cho mọi lời gọi mạng; circuit breaker và ba trạng thái; graceful degradation; phân biệt với availability, HA, reliability.
- Good to Know: bulkhead; load shedding; retry có backoff và giới hạn.
- Advanced: cơ chế cân bằng thích ứng, kiểm thử hỗn loạn. Chỉ cần biết rằng có công cụ chủ động gây lỗi để kiểm thử.
Tự kiểm tra
- Vì sao một cổng thanh toán chậm có thể làm trang chủ cũng ngừng phản hồi? Kỹ thuật nào ngăn điều đó?
- Mô tả ba trạng thái của circuit breaker và điều kiện chuyển giữa chúng.
- Khi mạch thanh toán mở,
gdev-shopnên làm gì thay vì trả lỗi cho khách?
Đáp án gợi ý
- Mọi request trang chủ có thể chờ lời gọi tới cổng thanh toán chậm, giữ luồng và kết nối trong lúc chờ. Khi các luồng bị chiếm hết, cả trang chủ không còn tài nguyên để phục vụ. Ngăn bằng timeout ngắn cho lời gọi mạng, circuit breaker ngừng gọi dịch vụ lỗi, và bulkhead cô lập tài nguyên riêng cho từng phụ thuộc.
- Closed: mọi lời gọi đi qua và đếm lỗi; vượt ngưỡng lỗi thì chuyển sang Open. Open: từ chối ngay không gọi dịch vụ; sau một thời gian chờ thì chuyển sang HalfOpen. HalfOpen: cho một vài lời gọi thử; thành công thì về Closed, thất bại thì về Open.
- Thay vì trả lỗi, nên suy giảm có kiểm soát: nhận đơn ở trạng thái chờ thanh toán, hiển thị thông báo thanh toán tạm bận, đưa vào hàng đợi để thử lại sau, hoặc đề xuất phương thức thanh toán khác. Khách vẫn hoàn tất được việc chính.
Nếu chỉ nhớ 3 điều
- Fault tolerance là hành xử có kiểm soát khi có lỗi, đặc biệt lỗi một phần: cô lập, không để lan.
- Mọi lời gọi mạng cần timeout; circuit breaker (Closed, Open, HalfOpen) biến chờ lâu thành thất bại tức thì để bảo vệ tài nguyên.
- Luôn có phương án khi lỗi (graceful degradation), và kiểm thử bằng lỗi thật.