Hành trình API 'lột xác': Từ 3 RPS đến 991 RPS và bài học xương máu về Scaling
Lê Lân
0
Hành Trình Vượt Qua Thách Thức Tải Thực Tế Của Một Dịch Vụ Backend Đơn Giản
Mở Đầu
Khi một dịch vụ backend đơn giản lần đầu tiên đối mặt với lưu lượng truy cập thực tế, kết quả thường không như mong đợi: hệ thống có thể bị sập. Có lúc là từ từ, có lúc lại đột ngột và kịch tính. Câu chuyện này kể về hành trình tôi tái thiết kế lại một API thu thập sự kiện (event collector API) qua bốn lần, để có thể chịu được tải thực tế và mở rộng từ 3 RPS (Request Per Second) lên tới 991 RPS, đồng thời giảm thời gian phản hồi từ 28 giây xuống chỉ còn 17 mili giây.
Qua mỗi lần cải tiến, tôi đã học hỏi được rất nhiều bài học về thiết kế hệ thống, độ bền vững và tối ưu hiệu suất.
Quá Trình Phát Triển Qua 4 Giai Đoạn Chính
1. Thiết Kế Ngây Thơ – Ghi Thẳng Vào PostgreSQL
Ban đầu, API được xây dựng theo cách đơn giản nhất: ghi trực tiếp tất cả sự kiện vào cơ sở dữ liệu PostgreSQL.
Ưu điểm: Triển khai nhanh, mô hình đơn giản.
Nhược điểm:
Không chịu nổi tải cao.
Thời gian phản hồi chậm, có khi tới hàng chục giây.
Dễ xảy ra lỗi kẹt truy cập dẫn đến sập hệ thống.
Bài học: Không thể chỉ phụ thuộc vào ghi trực tiếp DB khi lưu lượng truy cập tăng đột biến.
2. Tích Hợp Bộ Đệm Trong Bộ Nhớ (In-Memory Batching)
Để tăng tốc độ, tôi áp dụng kỹ thuật gom nhóm các yêu cầu thành các batch trong bộ nhớ trước khi ghi vào DB, giúp giảm số lần ghi và tải lên DB.
Ưu điểm:
Tăng tốc độ xử lý.
Giảm độ trễ so với ghi trực tiếp.
Nhược điểm:
Dễ mất dữ liệu nếu hệ thống gặp sự cố giữa chừng.
Rủi ro mất mát dữ liệu cao trong trường hợp server bị crash.
Chú ý: In-memory batching cải thiện tốc độ nhưng kém an toàn khi mất dữ liệu có thể xảy ra.
3. Sử Dụng Hàng Đợi Redis – Decoupling và Hiệu Năng
Tiếp theo đó, tôi chuyển sang sử dụng Redis làm hệ thống hàng đợi (queue) để tách biệt việc ghi sự kiện ra khỏi API nhận yêu cầu.
Ưu điểm:
Tách bạch rõ ràng giữa tiếp nhận và xử lý dữ liệu.
Giảm tải trực tiếp lên DB.
Server API trở thành stateless, dễ dàng mở rộng theo tầng ngang.
Nhược điểm:
Cần quản lý hệ thống Redis riêng biệt.
Vẫn có độ trễ do xử lý theo hàng đợi.
Ưu điểm Redis Queue
Nhược điểm Redis Queue
Độ bền cao hơn in-memory
Phức tạp hơn trong quản lý
Tối ưu hiệu năng
Có độ trễ trong xử lý
Điều quan trọng: Việc decoupling giúp hệ thống bền bỉ hơn khi đối mặt với luồng yêu cầu lớn.
4. Kiến Trúc Event-Driven Với Kafka + Flink – Xử Lý Phân Tán Thời Gian Thực
Cuối cùng, để đáp ứng nhu cầu mở rộng tối đa, tôi xây dựng lại toàn bộ hệ thống dựa trên kiến trúc event-driven sử dụng Kafka làm nền tảng streaming và Flink để xử lý luồng sự kiện thời gian thực.
Ưu điểm:
Khả năng mở rộng cực lớn, xử lý gần như tức thì.
Đảm bảo độ bền và không mất dữ liệu.
Xử lý luồng sự kiện phức tạp và realtime stream processing.
Ví dụ:
Cho phép xử lý hàng ngàn sự kiện trên giây với độ trễ cực thấp.
Kiến trúc event-driven là lựa chọn tối ưu cho các hệ thống cần xử lý dữ liệu khối lượng lớn và thời gian thực.
Tổng Kết Bài Học Rút Ra
Thử thách ban đầu: Thiết kế đơn giản dễ triển khai nhưng không bền vững với tải thực tế.
Batching trong bộ nhớ: Cải thiện tốc độ nhưng rủi ro mất dữ liệu.
Redis queue: Giải pháp decoupling, cải thiện độ bền và khả năng mở rộng.
Kafka + Flink: Kiến trúc cuối cùng đáp ứng được cả quy mô và yêu cầu phức tạp của hệ thống thời gian thực.
Mỗi lần cải tiến đều giúp hệ thống hoạt động ổn định hơn, đồng thời cung cấp những bài học quý giá về thiết kế hệ thống phân tán, tối ưu hiệu suất và đảm bảo độ bền bỉ trong vận hành thực tế.
Hành trình từ 3 RPS lên gần 1000 RPS với thời gian phản hồi giảm từ 28 giây xuống dưới 20 ms không chỉ là câu chuyện kỹ thuật, mà còn là minh chứng cho sự kiên trì học hỏi và đổi mới liên tục.