Timeout và retry trong web/app: đừng để một request chậm kéo sập cả trải nghiệm

15 lượt xem · 1 trả lời

1. 07:44 06/08/2026
Trong phát triển web/app, nhiều lỗi không đến từ code sai hoàn toàn mà đến từ việc hệ thống phải gọi sang dịch vụ khác: database, API thanh toán, email, SMS, storage, hoặc một service nội bộ. Nếu các lời gọi này bị chậm, treo hoặc thất bại tạm thời, ứng dụng có thể phản hồi rất lâu, người dùng bấm lại nhiều lần, còn server thì giữ kết nối ngày càng nhiều. Hai công cụ quan trọng để xử lý tình huống này là timeout và retry. Nghe đơn giản, nhưng nếu dùng không đúng, chúng có thể làm hệ thống tệ hơn. Timeout là gì và vì sao cần đặt rõ ràng? Timeout là giới hạn thời gian tối đa mà ứng dụng chờ một thao tác hoàn thành. Ví dụ: khi backend gọi API của cổng thanh toán, ta không nên chờ vô hạn. Nếu sau 5 giây chưa có phản hồi, hệ thống nên dừng chờ và trả về trạng thái phù hợp. Không đặt timeout có thể gây ra các vấn đề: - Request bị treo lâu, người dùng thấy ứng dụng “đang quay mãi”. - Server giữ tài nguyên như thread, connection, memory lâu hơn cần thiết. - Khi một dịch vụ phụ bị chậm, toàn bộ hệ thống chính cũng bị kéo chậm theo. Một nguyên tắc thực tế: mọi lời gọi ra ngoài tiến trình hiện tại nên có timeout. Gọi database, gọi HTTP API, gửi email, đọc file từ storage, gọi service nội bộ đều nên được cấu hình giới hạn thời gian. Retry là gì và khi nào nên dùng? Retry là thử lại một thao tác sau khi thất bại. Retry hữu ích với lỗi tạm thời, ví dụ: - Mạng chập chờn. - Service bên thứ ba trả lỗi 502, 503, 504. - Database tạm thời quá tải. Tuy nhiên, không phải lỗi nào cũng nên retry. Nếu API trả về lỗi 400 do dữ liệu không hợp lệ, thử lại 10 lần cũng không giúp gì. Nếu người dùng nhập sai mã giảm giá, retry chỉ làm tốn tài nguyên. Nên phân loại lỗi trước khi retry: - Có thể retry: timeout, lỗi mạng tạm thời, 502/503/504. - Không nên retry: 400, 401, 403, lỗi validation, lỗi nghiệp vụ rõ ràng. - Cần cẩn trọng: 500, vì có thể là lỗi tạm thời nhưng cũng có thể là bug thật. Đừng retry ngay lập tức quá nhiều lần Một lỗi phổ biến là retry liên tục không nghỉ: gọi thất bại, gọi lại ngay, lại thất bại, lại gọi tiếp. Khi nhiều request cùng làm vậy, service đang quá tải sẽ càng quá tải hơn. Thay vào đó, nên dùng backoff: mỗi lần retry thì chờ lâu hơn một chút. Ví dụ: - Lần 1: thử lại sau 200ms. - Lần 2: thử lại sau 500ms. - Lần 3: thử lại sau 1s. Có thể thêm jitter, tức là cộng thêm một khoảng ngẫu nhiên nhỏ, để tránh việc hàng loạt request retry cùng một thời điểm. Ví dụ thực tế: gọi API vận chuyển khi tạo đơn hàng Giả sử một website bán hàng cần gọi API hãng vận chuyển để tạo mã vận đơn. Luồng xử lý có thể như sau: 1. Người dùng bấm “Đặt hàng”. 2. Backend lưu đơn hàng vào database với trạng thái “chờ tạo vận đơn”. 3. Backend gọi API vận chuyển. 4. Nếu thành công, lưu mã vận đơn. 5. Nếu timeout hoặc lỗi tạm thời, đưa việc tạo vận đơn vào background job để thử lại sau. Cách này tốt hơn việc bắt người dùng chờ quá lâu ở màn hình thanh toán. Người dùng chỉ cần biết đơn hàng đã được ghi nhận, còn việc đồng bộ vận chuyển có thể xử lý tiếp ở phía sau. Lưu ý quan trọng: retry phải đi cùng idempotency Nếu thao tác có tác dụng phụ như tạo đơn hàng, trừ tiền, gửi email, tạo mã vận đơn, retry có thể gây trùng lặp. Vì vậy cần có cơ chế idempotency hoặc khóa định danh thao tác. Ví dụ khi gọi API thanh toán, mỗi yêu cầu nên có một mã giao dịch duy nhất. Nếu retry cùng mã đó, hệ thống thanh toán hiểu rằng đây vẫn là cùng một giao dịch, không phải một giao dịch mới. Một số lời khuyên có thể áp dụng ngay - Đặt timeout cho mọi HTTP client, database client và SDK bên thứ ba. - Không dùng một timeout quá lớn chỉ để “cho chắc”. Timeout lớn có thể che giấu vấn đề hiệu năng. - Chỉ retry với lỗi có khả năng tạm thời. - Giới hạn số lần retry, thường chỉ nên vài lần tùy tình huống. - Dùng backoff thay vì retry liên tục. - Với thao tác quan trọng, ghi lại trạng thái trung gian để có thể xử lý tiếp bằng background job. - Đảm bảo các thao tác có tác dụng phụ được thiết kế idempotent. - Log đủ thông tin khi timeout hoặc retry thất bại, nhưng không ghi dữ liệu nhạy cảm. Kết luận Timeout giúp hệ thống biết khi nào nên ngừng chờ. Retry giúp vượt qua lỗi tạm thời. Nhưng cả hai cần được thiết kế có kiểm soát. Một ứng dụng ổn định không phải là ứng dụng không bao giờ gặp lỗi, mà là ứng dụng biết giới hạn thời gian chờ, biết thử lại đúng lúc, và biết thất bại một cách an toàn khi dịch vụ phụ gặp vấn đề.
0 Facebook
2. 10:40 12/10/2026
Trả lời Quản Trị Viên Trong phát triển web/app, nhiều lỗi không đến từ code sai hoàn toàn mà đến từ việc hệ thống phải gọi sang dịch vụ khác: …
Bài viết rất thực tế, đặc biệt là phần nhấn mạnh không retry lỗi 4xx – một lỗi hay gặp ở các team mới. Mình thấy điểm "timeout phải đặt cho mọi lời gọi ra ngoài" nghe dễ nhưng triển khai đầy đủ khá vất vả, nhất là khi dùng nhiều thư viện third-party có default timeout lớn hoặc thậm chí không có timeout. Về backoff + jitter, mình thường dùng exponential backoff với full jitter (random 0 đến max_delay) thay vì fixed jitter, thấy hiệu quả hơn khi scale nhiều instance cùng retry một service đang down. Một chỗ mình hay đau đầu là xử lý idempotency key cho các background job retry sau khi timeout. Anh/chị nào có kinh nghiệm lưu key này ở đâu hiệu quả (Redis, DB, hay header request) cho trường hợp job worker crash giữa chừng không?
0
Vui lòng đăng nhập để tham gia trả lời chủ đề này.
Điều hướng nhanh

Bạn muốn khám phá điều gì?

Tìm chủ đề, bài viết hoặc đi thẳng đến một khu vực của cộng đồng.

Esc để đóng Ctrl K để mở lại