Triển khai kĩ thuật chống quá tải cho backend: Rate-limiting
Trong hệ thống web, sẽ có những trường hợp oái oăm trên thực tế khiến service đột ngột nhận được 1 lượng lớn request.
Có thể do bị tấn công spam, hoặc có thể một khung giờ nào đó do idol toptop livestream quảng cáo, nên lượng truy cập tăng đột biến, thì chúng ta xử lý vấn đề đó như thế nào? Ta có thể áp dụng rate-limiting.
💎 Vậy rate limitting là gì?
Rate-limit là một kĩ thuật giúp ngăn chặn việc user sử dụng service (route) nào đó quá nhiều, dẫn tới app quá tải, hoặc cũng có thể dùng coi như 1 chiến lược ngăn chặn spam, quá tải dịch vụ.
Ví dụ:
- Một người dùng chỉ có thể comment 10 lần trong 1 phút.
- Một thiết bị chỉ có thể tạo được 1 tài khoản trong 1 ngày.
- Một IP chỉ có thể đăng ký tài khoản mới 10 lần trong 1 ngày.
Lợi ích của Rate-limiting:
- Giảm chi phí vận hành.
- Ngăn chặn service bị quá tải.
- Chặn DDoS.
🥑 Rate-limit dựa vào thời gian như thế nào?
Đã rate-limit thì sẽ phải dựa vào thời gian: 10 giây, 1 phút, 2 ngày, 1 tuần...
Vậy ta sẽ có 2 thuật ngữ:
- Time-windowing: Phân chia khoảng thời gian
- Time-overlapping: Các request dậm chân nối đuôi nhau.
Khi đó thì sẽ lòi ra vấn đề: Chẳng hạn trong time-window 1 phút chúng ta cho phép tối đa 1000 request, tuy nhiên 900 request đến vào lúc 10 giây cuối trong time-window đó và các request còn lại được nhảy sang window tiếp theo thì xử lý như thế nào? Đó là
⚔️ Các thuật toán dùng cho rate-limiter
Ta có vài thuật toán phổ biến cho rate-limiter (bộ giới hạn) như sau:
- Fixed Window Counter
- Sliding Window Log
- Sliding Window Counter
- Token Bucket
- Leaky Bucket
Với mỗi thuật toán sẽ ảnh hưởng tới cách phân chia time-window và xử lý overlapping khác nhau, các bạn có thể nghiên cứu thêm để áp dụng cho phù hợp với dự án và requirement.
🍧 Nên đặt rate-limiter ở đâu?
Thường ta có thể đặt rate-limiter ở interceptor (middleware) ở từng service, hoặc recommend hơn là nên đặt nó ở layer API Gateway.
Chúng ta cũng có thể dùng bộ cache như redis để lưu các giá trị của từng bộ limit cho user, nhằm giảm độ trễ cho request đó. Khi cache, thường thì sẽ cần xác định key của bộ limit đó, lúc đó thì 1 giá trị thường dùng là: {user_id}_{service_route}
, nếu nằm trong cluster thì service route có thể là FQDN (dạng service-name.cluster.svc.local
)
🚀 Load shedding
Ngoài Rate-limiting thì chúng ta có thể mở rộng bộ giới hạn ra cho toàn bộ service thay vì 1 user, chúng ta sẽ có load shedding.
Tưởng tượng, máy chủ của bạn có thể xử lý trơn tru tối đa 1000 request trong vòng 1 giây. Nếu request thứ 1001 xảy đến, server có khả năng sẽ bị chậm hoặc tệ hơn là quá tải, crash luôn. Service crash luôn luôn là tối kị lúc vận hành và design.
Load shedding là một kĩ thuật giúp bạn "kiềm chế" lại những request như em số 1001 đó vậy.
Để làm được load shedding, bạn phải ước lượng được sức tải của server, và tạm ngưng hoặc chặn lại các request overload.
Cách để thực thi load shedding chính là dùng 1 bộ đếm ở tầng route, khi số lượng request vượt quá mức cho phép, ta không tiếp tục chuyển request đi xử lý mà trực tiếp trả về response với status code 503: Service Unavailable (fail fast).
✍️ Kết luận
Rate-limiting hay rộng hơn là load-shedding là kĩ thuật giúp bảo vệ server, chống quá tải và spam rất hiệu quả, nhưng nó không thể ngăn chặn được DDoS 100% mà cần kết hợp những kĩ thuật khác nữa.
Vì một khi đã tiếp nhận request, tức là máy chủ đã phải bỏ ra một lượng resource nhất định để thiết lập HTTP, TLS handshake. Do đó, load shedding và rate-limiting tuy rất hữu dụng, lại không thể ngăn chặn trường hợp lượng request tăng mãi tăng mãi, thì bùmmm, service vẫn sẽ tiêu đời thôi.
Giống như việc quầy bán hàng mặc dù không tiếp nhận đơn hàng mà từ chối đi chăng nữa thì nhân viên khu lễ tân vẫn phải cúi chào khách hàng, tiễn khách đi vậy, cúi mãi cúi mãi thì nhân viên sẽ bị thoái hoá đốt sống cổ thôi 😂.
Tham khảo thêm: