Have you ever wondered how Uber is able to support, satisfy and delight 95 million active users, successfully complete 1.44 billion rides per year, and generate $26 billion of bookings per year?
We did, and this is the reason we are working on this blog series to understand and decode how Uber’s backend works and operates. This is part 2 of the blog series to understand Uber’s Service-oriented architecture and design.
In terms of scale and operational excellence, Uber’s endeavors to ensure billions of smooth rides for millions of rides every single day is simply mind-blowing. The very fact that around 25% of Americans are using Uber at least once a month shows how important Uber is for the public transport industry, and how mainstream Uber has become, in just 13 years of its existence.
By understanding and more importantly, appreciating Uber’s backend process, and their advanced service-oriented architecture, we can develop and launch better mobile apps, and empower millions of users, all across the world.
In part 1 of this series, we understood why Uber adopted a service-oriented architecture, the relevance of DISCO or Dispatch Optimization, and how Uber is able to match a rider with the driver in the shortest possible time, and distance by leveraging the features of Google S2 Library for geo-positioning.
In part 2 of the series, we will decode the entire system design of Uber's backend process, delve deeper into the various components of the system design and find out what happens when a user requests a driver. We will also learn more about Uber’s Geo-Spatial Design.
Without much ado, let’s dive straight into it!
Uber’s System Design
Here’s the diagram of the system design, of Uber’s backend process, and it shows how Uber is able to connect a rider with the driver, and how a trip is successfully completed, taking into consideration geographic locations and data.
In the diagram, a cab is a supply, and the user is the demand.
Now, every 4 seconds, the cab will share its location data to the KAFKA REST API, a specialized API that manages messages between two entities.
This messaging from cab to the API is handled via Firewall, over to the Load Balancer, and then finally to KAFKA, and from there, to different servers.
One copy of this call is also shared with the DISCO or Dispatch Optimization so that the latest position and status of the cab are known to it.
Here’s a brief description of the various components:
Web Application Firewall (WAF): This component is mainly used for security and blocking purposes: The system can safely block requests from banned IPs, bots and the regions where Uber is not supported.
Load Balancer: There are mainly three different layers of Load Balancers: Layer 3, Layer 4 and Layer 7.
While Layer 3 receives all the traffic from the approved IPs, Layer 4 is used mainly for DNS-based Load Balancing, Layer 7 is used for Application-level Load Balancing.
KAFKA REST API: As shared earlier, KAFKA REST API works as the end-point for receiving messages from the supply, that is the cabs. In case there are 5000 cabs operating in the region, then KAFKA REST API will receive 5000 location messages from the cabs, every 4 seconds. Concurrently KAFKA REST API will share these live location messages with the DISCO to keep consistency in the system and dispatch process.
Web Socket: In order to share synchronized messages between any Client and a Server, Web Sockets are deployed. In case it’s a normal HTTP request, then web sockets may not be used, but since Uber is handling all messages between the demand and supply in real-time, Web Sockets are an important part of the system design.
Primarily written in NodeJS (Asynchronous and event-driven framework), Web Socket helps to establish a connection between the Cab application and the server, and the User application and the server.
DISCO Component: This is also written in NodeJS for supporting real-time information exchange regarding the dispatching of the nearest cabs for the end-users.
In order to scale DISCO servers and functionalities, Uber app developers have deployed Ringpop, an open-source library to make all their servers extremely scalable and productive. In the case of DISCO, Ringpop uses two functionalities:
Consistent Hashing and RPC Call.
With the Consistent Hashing function, the work is evenly distributed between the servers for maximum productivity; and with RPC Call, the servers can seamlessly communicate with each other. Occasionally, a SWIM protocol/Gossip Protocol is deployed so that the servers are able to know and acknowledge the responsibilities of the other servers, and distribute/execute the messages and tasks accordingly. With the assistance of this special protocol under DISCO, servers can be added or removed from the ring: If a new server is added, then the tasks are distributed to the new server, and if a server is removed, then the existing tasks are distributed among the existing servers.
This way, Uber is never disrupted due to scaling issues.
What Happens When Rider Requests A Cab (Demand-Supply Match)
Here’s an overview of what happens when a user requests a cab for going from point A to point B:
- Demand server requests supply server, filtered by the location ID
- The supply server locates a nearby cab
- A request is placed to cabs for allocation
- After a period of time, the request is sent to another cab (if the initial request fails)
- Once matching is completed, the user is notified
Although this entire process seems simple, a lot of things are happening in the backend within the system design, to ensure 100% success.
This is how it happens:
As soon as the rider or the user fires his/her Uber app, and requests a cab, then that request lands on the Web Socket, which dispatches it immediately to the Demand Service.
Now, the Demand Service is aware that this request is from a Cab or from a rider asking for the Ride. In this case, since the request is coming from a rider for a cab, Demand Service dispatches the request to the Supply Service, with all the information needed: Which type of ride is requested, how many rides are needed, and the location where this ride is needed.
Armed with this information, the Supply Service requests one of the servers in the server ring to fulfill this request. The Supply Service shares the User ID and Location ID for this purpose.
Based on this information, the DISCO gets activated and tries to match the nearest cab available to the rider (as shared in Part 1 of this series), based on the ETA (expected time of arrival), and the distance from the rider.
Once confirmed, the web socket is again activated, and it sends the message to the cabs, asking if they want to accept this ride request.
Once the cab says yes, the ride is successfully matched, and the rider is notified.
If you are looking for Uber app developers and Rideshare app developers who can develop and launch similar mobile apps with service-based architecture, then we can help you.
We at TechAhead have a 200+ strong team of passionate and talented programmers, developers, designers, system architects, project managers, and business analysts who can understand your specific requirements, and help you build an app like Uber.
Here’s your chance to disrupt the mobile ecosystem with stunningly powerful and aesthetic mobile apps, with unprecedented scalability and features.
Connect with us right here.