Bài toán: Hàng triệu tài xế, mỗi 4 giây
Grab có khoảng 5 triệu tài xế hoạt động tại Đông Nam Á. Uber có hơn 5 triệu tài xế toàn cầu. Nếu mỗi tài xế gửi tọa độ GPS mỗi 4 giây, hệ thống phải nhận:
5.000.000 tài xế ÷ 4 giây = 1.250.000 gói tin GPS / giây
Đây là 1.25 triệu write operations mỗi giây — chỉ riêng cho dữ liệu vị trí, chưa tính các request khác. HTTP REST truyền thống không thể xử lý tải này hiệu quả.
Tại sao HTTP REST không phù hợp?
Chi phí kết nối (Connection Overhead)
Mỗi HTTP request yêu cầu:
- TCP Handshake (3-way handshake)
- TLS Handshake (thêm 1-2 round trips nếu dùng HTTPS)
- HTTP Headers (~200-800 bytes overhead cho mỗi request)
- Đóng kết nối (hoặc keep-alive timeout)
Với GPS payload chỉ khoảng 50-100 bytes (latitude, longitude, timestamp, speed, heading), overhead của HTTP chiếm gấp 5-10 lần dữ liệu thực.
Tốn pin điện thoại
Mỗi lần thiết lập kết nối HTTP mới, chip radio (4G/5G) của điện thoại phải chuyển từ trạng thái ngủ (idle) sang trạng thái hoạt động (active), tiêu tốn năng lượng đáng kể. Nếu tài xế chạy 8-12 tiếng/ngày, pin sẽ cạn rất nhanh.
Giải pháp: Các giao thức siêu nhẹ
1. MQTT (Message Queuing Telemetry Transport)
MQTT được thiết kế ban đầu cho các thiết bị IoT với băng thông hạn chế và pin nhỏ — hoàn hảo cho việc gửi tọa độ GPS liên tục.
Tại sao MQTT hiệu quả:
- Kết nối mở liên tục (Persistent Connection): Chỉ handshake một lần, sau đó gửi tọa độ liên tục mà không cần thiết lập lại kết nối.
- Header cực nhỏ: Chỉ 2 bytes overhead so với hàng trăm bytes của HTTP.
- Quality of Service (QoS) linh hoạt:
- QoS 0: Gửi và quên (fire-and-forget) — phù hợp cho GPS vì vài giây sau lại có tọa độ mới.
- QoS 1: Đảm bảo gửi ít nhất một lần.
- Last Will and Testament (LWT): Nếu tài xế mất kết nối đột ngột, MQTT broker tự động thông báo cho server rằng tài xế đã offline.
Luồng dữ liệu MQTT:
Driver App → MQTT Publish → MQTT Broker Cluster → Kafka
Topic: "driver/{driver_id}/location"
Payload: {
"lat": 10.7769,
"lng": 106.7009,
"ts": 1717689600,
"speed": 35.2,
"heading": 127,
"accuracy": 8
}
2. gRPC Bidirectional Streaming
Uber sau này chuyển sang dùng gRPC Streams (và QUIC/HTTP3) thay vì MQTT cho nhiều luồng dữ liệu real-time.
Ưu điểm so với MQTT:
- Protobuf serialization: Payload nhị phân nhỏ hơn JSON 3-10 lần.
- Bidirectional: Server có thể đẩy dữ liệu ngược lại (cuốc xe, chỉ đường) trên cùng kết nối.
- Flow control: gRPC có cơ chế kiểm soát luồng (backpressure) tích hợp, quan trọng khi server bị quá tải.
- Multiplexing: HTTP/2 cho phép nhiều stream trên một kết nối TCP, tránh head-of-line blocking.
// Định nghĩa service gRPC cho Location Streaming
service LocationService {
// Tài xế gửi stream tọa độ liên tục
rpc StreamLocation(stream LocationUpdate) returns (stream ServerCommand);
}
message LocationUpdate {
double latitude = 1;
double longitude = 2;
int64 timestamp_ms = 3;
float speed_mps = 4; // mét/giây
float heading_degrees = 5; // hướng đi (0-360)
float accuracy_meters = 6; // độ chính xác GPS
string driver_id = 7;
}
message ServerCommand {
oneof command {
NewRideOffer ride_offer = 1;
NavigationUpdate nav = 2;
PingRequest ping = 3;
}
}
Kỹ thuật tối ưu trên Mobile App
Batching — Gom nhiều tọa độ
Thay vì gửi mỗi tọa độ riêng lẻ, app gom (batch) 3-5 điểm GPS rồi gửi một cục:
Không batching: Có batching:
GPS 1 → Send GPS 1 ─┐
GPS 2 → Send GPS 2 ─┤ → Send 1 batch
GPS 3 → Send GPS 3 ─┘
GPS 4 → Send GPS 4 ─┐
GPS 5 → Send GPS 5 ─┤ → Send 1 batch
GPS 6 → Send GPS 6 ─┘
6 network calls 2 network calls (tiết kiệm 67%)
Adaptive Frequency — Tần suất thích ứng
App không cần gửi tọa độ đều đặn mỗi 4 giây. Nó điều chỉnh tần suất dựa trên ngữ cảnh:
| Trạng thái tài xế | Tần suất gửi GPS | Lý do |
|---|---|---|
| Đang chờ khách (idle) | Mỗi 15-30 giây | Tiết kiệm pin, tài xế không di chuyển nhiều |
| Đang di chuyển tìm khách | Mỗi 4-5 giây | Cần cập nhật vị trí cho matching |
| Đang chở khách (on-trip) | Mỗi 2-3 giây | Cần độ chính xác cao cho ETA và bản đồ |
| Tốc độ cao (>60 km/h) | Mỗi 1-2 giây | Di chuyển nhanh, vị trí thay đổi nhiều |
| Đứng yên (dừng đèn đỏ) | Tạm ngừng gửi | GPS không thay đổi, tiết kiệm 100% |
Dead Reckoning — Nội suy vị trí
Giữa hai lần nhận GPS, app khách hàng dùng Dead Reckoning (dự đoán quỹ đạo) để hiển thị xe chạy mượt mà trên bản đồ:
Vị trí GPS thực tế nhận được: ● (mỗi 4 giây)
Vị trí nội suy (interpolated): ○ (mỗi 100ms)
●───○───○───○───○───○───○───●───○───○───○───○───●
t=0 t=4s t=8s
Công thức nội suy đơn giản:
predicted_lat = last_lat + (speed × cos(heading) × Δt)
predicted_lng = last_lng + (speed × sin(heading) × Δt)
Lọc nhiễu GPS: Kalman Filter
Tín hiệu GPS từ điện thoại thường bị nhiễu (noisy) — đặc biệt trong thành phố có nhiều tòa nhà cao tầng, tín hiệu bị phản xạ (urban canyon effect). Kết quả: xe hiển thị trên bản đồ bị nhảy lung tung hoặc chạy xuyên qua nhà.
Kalman Filter hoạt động như thế nào?
Kalman Filter là thuật toán dự đoán → hiệu chỉnh liên tục:
Vòng lặp mỗi khi nhận GPS mới:
1. PREDICT (Dự đoán):
Dựa trên vị trí, tốc độ, hướng đi trước đó,
dự đoán vị trí hiện tại bằng mô hình vật lý.
predicted_position = previous_position + velocity × Δt
2. UPDATE (Hiệu chỉnh):
So sánh dự đoán với GPS thực tế vừa nhận.
Tính trọng số: tin dự đoán hay tin GPS hơn?
Nếu GPS có accuracy = 5m (chính xác): tin GPS nhiều hơn
Nếu GPS có accuracy = 50m (nhiễu): tin dự đoán nhiều hơn
final_position = weighted_average(predicted, gps_measured)
Lyft Engineering mô tả trong blog kỹ thuật của họ rằng họ dùng Marginalized Particle Filter — phiên bản nâng cao hơn Kalman Filter — có thể theo dõi nhiều quỹ đạo khả dĩ đồng thời khi không chắc chắn xe đang ở đường nào (ví dụ: đường chính hay đường song hành).
Map Matching — Bắt xe về đúng đường
Sau khi lọc nhiễu, hệ thống thực hiện Map Matching — ghép tọa độ đã lọc vào mạng lưới đường bộ số hóa (digital road network):
GPS thô (sau Kalman): Map Matched:
○ ○ ●──●
○ ○ ● ●
○ ○ ──────► ● ●
○ ○ ● ●
○ ○ ●──●
(Tọa độ nằm rải rác) (Bám sát lòng đường)
Kiến trúc Location Pipeline hoàn chỉnh
Driver Phone GPS Sensor
│
▼
┌───────────────┐
│ Kalman Filter │ (Trên điện thoại)
│ + Batching │
└───────┬───────┘
│ gRPC Stream / MQTT
▼
┌───────────────┐
│ Load Balancer │ (Nginx / Envoy)
└───────┬───────┘
│
▼
┌───────────────┐
│ Location │ → Validate, enrich, convert to H3 index
│ Service │
└───────┬───────┘
│ Produce to Kafka
▼
┌───────────────┐
│ Apache Kafka │ Topic: "driver.location.updates"
└───┬───┬───┬───┘
│ │ │
▼ ▼ ▼
Redis Flink Data Lake
GEO (Stream (S3/HDFS
Cache Processing) Analytics)
Các con số thực tế
| Metric | Giá trị ước tính |
|---|---|
| Số lượng tài xế online đồng thời (Grab SEA) | ~1.5 triệu |
| Tần suất gửi GPS trung bình | Mỗi 4 giây |
| Throughput Location Service | ~375.000 msg/s |
| Kích thước payload GPS (Protobuf) | ~40-60 bytes |
| Băng thông GPS raw | ~15-22 MB/s |
| Latency end-to-end (phone → Redis) | < 200ms |
Tiếp theo, chúng ta sẽ tìm hiểu cách Uber dùng thuật toán H3 để chia bản đồ thành hàng triệu ô lục giác và tìm tài xế gần nhất trong nháy mắt. Đọc tiếp Phần 2 — Geospatial Indexing: H3, S2 Geometry & Redis GEO.