Uber’s Rate Limiting System: Cách Uber Bảo Vệ Hệ Thống 80 Triệu RPS
Bên trong Global Rate Limiter của Uber: kiến trúc phân tán, probabilistic dropping và tự động hoá cấu hình ở quy mô toàn cầu.
Mục lục
Khi hệ thống xử lý hàng trăm triệu RPC mỗi giây
Uber vận hành một kiến trúc microservices khổng lồ.
Hệ thống của họ xử lý hàng trăm triệu RPC mỗi giây, trải dài trên hàng nghìn service và nhiều region.
Trong một môi trường như vậy, chỉ cần một service bị spam request, hoặc một caller lỗi retry liên tục, toàn hệ thống có thể sập dây chuyền.
Vì vậy, rate limiting không chỉ là tính năng phụ.
Nó là lớp bảo vệ sống còn.
Uber xây dựng Global Rate Limiter (GRL) để giải quyết bài toán này ở quy mô toàn cầu.
Vì sao rate limiting quan trọng trong distributed systems?
Trong hệ thống phân tán, rate limiting giúp:
- Ngăn overload
- Tránh cascading failure
- Đảm bảo fairness giữa các caller
- Giữ latency ổn định khi có spike
Thiết kế phổ biến nhất là dùng Redis làm distributed counter.
Mỗi request sẽ increment một key. Nếu vượt threshold → reject.
Cách này đơn giản và hiệu quả cho nhiều hệ thống.
Nhưng ở quy mô Uber, nó không còn khả thi.
Vấn đề với cách làm cũ
Trước khi có GRL, mỗi team ở Uber tự implement limiter riêng.
Kết quả:
- Mỗi nơi một kiểu
- Cấu hình không đồng nhất
- Phải redeploy để đổi limit
- Redis gây thêm latency
- Khó debug khi có incident
Tệ hơn, nếu dùng Redis làm centralized counter ở quy mô hàng trăm triệu request mỗi giây:
- Tăng network hop cho mỗi request
- Cross-region consistency cực khó
- Phải vận hành hàng trăm cluster Redis
- Thêm nhiều failure mode
Uber nhận ra:
Muốn scale thật sự, enforcement phải local.
Không thể phụ thuộc vào một central counter.
Tầm nhìn: Rate limiting ở tầng hạ tầng
Giải pháp của Uber là đưa rate limiting vào service mesh.
Điều đó có nghĩa:
- Không cần thay đổi code service
- Mọi request đều đi qua proxy
- Enforcement xảy ra ở data plane
Mục tiêu rất rõ ràng:
Bất kỳ team nào cũng có thể cấu hình limit theo caller hoặc procedure
mà không cần sửa code.
Kiến trúc 3 tầng của GRL
GRL hoạt động theo mô hình phân cấp:
-
Client (proxy trong service mesh)
- Quyết định allow/drop tại chỗ
- Report RPS lên aggregator
-
Aggregator (theo zone)
- Tổng hợp số liệu từ các host
- Gửi lên controller
-
Controller (theo region/global)
- Tính toán global utilization
- Gửi lại drop ratio xuống dưới
Điểm quan trọng:
Quyết định drop được thực hiện local,
nhưng dựa trên dữ liệu global.
Nếu control plane chết → fail open.
Uber ưu tiên availability thay vì tự bóp chết hệ thống.
Token Bucket: Bước khởi đầu
Ban đầu, Uber dùng token bucket.
Mỗi proxy:
- Tự giữ token
- Refill theo tỷ lệ load so với global limit
- Hết token → drop
Họ cho phép giữ token tối đa 10–20 giây để xử lý burst.
Nhưng production cho thấy vấn đề:
- Không chia đều capacity khi số caller quá nhiều
- Host có burst local có thể drop sớm dù global chưa full
- Khó đảm bảo fairness toàn cục
Token bucket local không phản ánh được global state chính xác.
Drop-by-Ratio: Bước ngoặt quan trọng
Uber chuyển sang mô hình drop theo tỷ lệ xác suất.
Nếu tổng RPS = 1.5 × limit
→ drop khoảng 33% request.
Công thức:
drop_ratio = (actual_rps - limit_rps) / actual_rps
Drop được áp dụng probabilistic trên mọi instance của caller.
Kết quả:
- Throttle đồng đều
- Không phụ thuộc trạng thái từng host
- Fair hơn cho gateway service có hàng nghìn instance
Đây là bước tiến lớn về mặt global coordination.
Thiết kế cuối cùng: Probabilistic Dropping hoàn toàn
Sau đó Uber loại bỏ hoàn toàn token bucket.
Hiện tại:
- Mọi quyết định dựa trên aggregated load
- Control plane cập nhật drop ratio mỗi giây
- Client chỉ cần sampling xác suất
Hot path cực nhẹ:
- Không counter local
- Không network call per request
- Không sync phức tạp
Trade-off duy nhất:
Có độ trễ 2–3 giây giữa spike và enforcement.
Trong thực tế, độ trễ này chấp nhận được.
Kết quả thực tế
Sau khi migrate từ Redis-based limiter sang GRL:
- P50 giảm ~1ms
- P90 giảm hàng chục ms
- P99.5 giảm đến 90%
Một service sống sót qua spike 15× traffic
(22K → 367K RPS) mà không sập.
GRL hiện xử lý khoảng 80 triệu request mỗi giây
trên hơn 1100 service.
Đây không còn là tool phụ trợ.
Nó là nền tảng reliability của Uber.
Tự động hoá cấu hình: Rate Limit Configurator (RLC)
Sau khi enforcement đã ổn định, vấn đề mới xuất hiện:
Cấu hình limit vẫn phải chỉnh tay.
Ở quy mô Uber:
- Service thay đổi liên tục
- Traffic pattern thay đổi hàng tuần
- YAML config nhanh chóng lỗi thời
Giải pháp là RLC.
RLC:
- Thu thập metric nhiều tuần
- Tính toán safe limit có buffer
- Rewrite config tự động
- Push xuống GRL qua control plane
Vòng lặp này giúp hệ thống tự thích nghi với traffic thực tế.
Shadow Mode: An toàn trước khi enforce
Uber cho phép chạy limit ở chế độ shadow:
- Tính toán drop giả định
- Không thực sự drop
- Quan sát dashboard
Nhờ đó team có thể tự tin trước khi bật enforcement.
Điều đáng học nhất ở đây
Điểm hay nhất của GRL không phải thuật toán.
Mà là cách Uber:
- Di chuyển logic khỏi business layer
- Đưa enforcement xuống infrastructure
- Tối giản hot path
- Dùng probabilistic thay vì counter đồng bộ
- Tự động hoá cấu hình thay vì chỉnh tay
Họ không chọn giải pháp “đúng về lý thuyết”.
Họ chọn giải pháp phù hợp với quy mô thực tế.
Kết luận
Rate limiting ở quy mô nhỏ là kỹ thuật.
Rate limiting ở quy mô Uber là hạ tầng sống còn.
GRL cho thấy một hệ thống tốt không chỉ:
- Chặn overload
- Giảm latency
- Tăng fairness
Mà còn phải:
- Dễ vận hành
- Tự động thích nghi
- Không phụ thuộc vào con người