A distributed ride-sharing backend built with gRPC and Protocol Buffers, modelling core Uber-like functionality — rider requests, driver matching, real-time notifications, and multi-server load balancing — secured end-to-end with mutual TLS.
- Overview
- Architecture
- Setup
- Running the Service
- Load Balancing
- Security
- Service Functions
- Benchmarks
Multiple riders and drivers join the system and register themselves. When a rider requests a ride, the request is routed to a server using one of several load-balancing strategies. The server selects an available driver and notifies them in real time. The driver has a timeout window to accept or reject the ride; on rejection or timeout, the server re-allots the ride to another driver, retrying up to 3 times before cancelling. All connections are secured using TLS certificates and role-based interceptors.
[ Rider Client ] [ Driver Client ]
| |
| gRPC (mTLS) | gRPC (mTLS)
↓ ↓
[ Server 1 ] ←——→ [ Server 2 ] ←——→ [ Server N ]
↕ ↕
[ rides.json | available_drivers.json ] ← shared persistent state
Servers self-register their port to server_ports.txt on startup and remove themselves on shutdown. Clients poll this file every 5 seconds to discover new or removed servers dynamically.
- Python 3.8+
grpcio,grpcio-tools,geopy,protobuf
pip install grpcio grpcio-tools geopy protobufbash generate_certs.shThis creates a self-signed CA, a server certificate, and separate client certificates for riders and drivers — all placed in the certificate/ directory.
python -m grpc_tools.protoc -I./protofiles --python_out=./protofiles \
--grpc_python_out=./protofiles ./protofiles/myuber.protopython server.pyEach instance auto-selects a free port starting from 50051 and registers itself. Run multiple instances in separate terminals to simulate a multi-server cluster.
python client.pySelect a role at startup:
| Input | Role | Capabilities |
|---|---|---|
r |
Rider | Request rides, check ride status |
d |
Driver | Register, subscribe to notifications, accept/reject/complete rides |
data |
Observer | View status of all current rides and drivers |
RequestRide
│
▼
allot_driver ──────────────────► Driver notified (server-streaming)
│ │
▼ accept / reject / timeout
cancel_ride_if_pending │
(after 1000s) ▼
re_allot_driver (up to 3 retries)
│
▼
CANCELLED if all retries fail
- Driver response timeout: 30 seconds
- Overall ride timeout: 1000 seconds
- Max re-allotment retries: 3
Three strategies are implemented in the client stub pool:
Always routes to the first available server stub. Simple but uneven under concurrent load.
Cycles through all server stubs in order. The current index is persisted in idx.txt, making it consistent across multiple client processes.
Hashes a provided key (e.g. rider ID) using MD5 and maps it to a server. Ensures the same key always routes to the same server, which improves data locality and minimises redistribution when the server set changes.
All gRPC channels use mutual TLS (mTLS) — both client and server present certificates signed by a shared CA.
Certificate layout:
| File | Purpose |
|---|---|
ca.crt / ca.key |
Self-signed Certificate Authority |
server.crt / server.key |
Server identity |
rider.crt / rider.key |
Rider client identity |
driver.crt / driver.key |
Driver client identity |
Two server-side interceptors run on every incoming RPC:
AuthorizationInterceptor — enforces role-based access by reading the role metadata header sent by the client:
- Only
ridermay callRequestRide - Only
drivermay callAcceptRide,RejectRide,CompleteRide,GetAvailableRides
LoggingInterceptor — logs the method name, timestamp, and caller role for every RPC call.
| Function | Description |
|---|---|
RequestRide |
Creates a new ride, assigns a ride ID, and initiates driver allotment |
SubscribeToRides |
Server-streaming RPC; pushes real-time RideNotification messages to subscribed drivers |
UpdateDriverStatus |
Sets a driver's status to AVAILABLE or BUSY |
AcceptRide |
Driver accepts an assigned ride; updates ride and driver status |
RejectRide |
Driver rejects a ride; triggers re-allotment |
CompleteRide |
Marks a ride as completed; returns driver to AVAILABLE |
GetRideStatus |
Returns the current status of a specific ride |
CheckDriverStatus |
Returns the current status of a specific driver |
GetDriverRiderStatus |
Returns a full table of all rides with their rider, driver, and status info |
allot_driver |
Randomly selects an available driver and notifies them |
re_allot_driver |
Retries driver assignment up to 3 times on rejection or timeout |
handle_driver_timeout |
Fires when a driver exceeds the 30s response window; triggers re-allotment |
cancel_ride_if_pending |
Cancels a ride that remains unaccepted after 1000s |
| Function | Description |
|---|---|
get_next_stub |
Round-robin server selection using a shared file-backed index |
hash_stub |
Consistent hashing server selection for a given key |
consistent_hash |
MD5-based hash mapped to a server port index |
subscribe_to_rides |
Opens a long-lived stream to receive ride notifications from a server |
periodic_update |
Background thread; refreshes server list every 5 seconds |
Tested with 500 ride requests across load balancing strategies:
| Strategy | Avg Response Time |
|---|---|
| Consistent Hashing | 170 ms |
| Round-Robin | 198 ms |
| Pick-First | 240 ms |
Consistent hashing outperforms the others by maintaining request locality — the same rider is always routed to the same server, reducing cache misses and coordination overhead.