Asynchronous processing and its benefits
In Asynchronous processing, we can move to the next task before the previous one finishes. So unlike synchronous processing which blocks the thread until the task is completed, here can perform multiple tasks simultaneously.
Consider the example of a pizza shop,
In a pizza shop, multiple clients requests pizzas and then they get their responses (whether the order has been placed or not) immediately. After getting an acknowledgment clients can do some other tasks until their order is done. Once we get the orders we can add them to the queue. The kitchen then takes orders from this queue one by one and makes the pizzas. Once the pizzas are done, clients are notified.
Notice that in this architecture, clients do not wait until the pizzas are done. It can perform some other tasks. This is called asynchronous processing.
Message Queue
A message queue is a component that provides an asynchronous communication protocol, which is a system that puts a message onto a message queue and does not require an immediate response to continue processing.
Consider the above example of the pizza shop. We use a message queue to store the orders. The kitchen then takes the orders from this queue one by one. Now even if we get order requests greater than the capacity of the kitchen, the queue can keep the order until the kitchen takes the order. So in simple terms message queues and asynchronous processing make our system highly available.
Publisher-Subscriber Model
Before we discuss the event-driven architecture let's first discuss the drawbacks of using request-response architecture using the example below:
In this system,
- Server 1 gets the request, processes it and persists the result in the database.
- It then sends requests to server 2 and server 3.
- Server 3 processes the data and then sends requests to servers 4 and 5.
The order of the requests does not matter so we can send the requests asynchronously.
-
First, there is a strong coupling between the services. Server 1 must know the details of servers 2 and 3 before sending the requests and so does server 3.
-
Suppose server 4 fails. Then server 3 waits for the response until it timeouts and sends the error message to server 1 and then server 1 sends it to the client. It took a lot of time to send the error message to the client. So there is high failure latency.
-
Suppose Server 1 and 2 had processed the data but it failed due to server 4. Now if the user sends the request again then server 1 will have stale data and so does server 2. After processing the request server 1 will update the data but server 2 still has stale data. This causes inconsistency.
To overcome these issues we can use Publisher-Subscriber Model
In a Publisher-Subscriber Model, instead of sending a request and waiting for a response, we publish the message to a message queue. Once the message has been published we send a success response to the client. To get the messages, services can subscribe to the message queue.
In this case, Server 2 subscribes message queue to get the messages from server 1. And servers 3 and 4 subscribe to the message queue to get messages from server 2.
Advantages of using the Publisher-Subscriber Model
-
It decouples the system. Server 1 doesn't need to know the exact details of servers 2 and 3. It just needs to publish messages to the message queue
-
It simplifies interactions in the system. Instead of having multiple points of failure, there is only a single point of failure (which is easier to deal with).
-
It also provides transaction guarantees. If the message is published to the message queue then it will reach its destination. Until the message reaches its destination message queue persists in the data.
-
It is more scalable. When adding a new service, it just subscribes to the message queue.
Disadvantages of using the Publisher-Subscriber Model
- It can cause data inconsistency
- It does not guarantee idempotency
Event-Driven Architecture
The main difference between event-driven and request-response architecture is in event-driven systems, services do not talk to each other directly. Instead, they use events to say "something has changed". So if a service sends an event to the event bus, it is saying "something has happened and if it concerns anyone else, you can consume this event".
Advantages of using an event-driven architecture
-
When a service gets data from the event bus it persists that data in its database. So even if the publisher fails, the subscriber can use its data. It makes the system more available.
-
Each service has its events log so it can move to any point in history. Therefore it provides easy rollback.
-
It is easy to replace services. The new service just needs to subscribe to the event bus and copy all the logs from the old service.
-
It gives you a transaction guarantee. Messages that are sent are at least once or at most once.
-
In this architecture, you are also storing the intent of the event. So, if we have a new service we can look at the intent of the data and make changes in the code such that you have a different state in the new service.
Disadvantages of using an event-driven architecture
-
Since each service has its data some data might be not updated and it becomes stale. So it makes our system less consistent.
-
It is very difficult to replace services at the gateway. For example, we have an email service that sends emails to an external service. We cannot replace this service because when we are sending emails we are dependent on the responses from the external systems and if that response is dependent on time then when we are replacing the service and replaying the events those events will get different responses (we won't get the exact logs). So all services at the gateway need to store the timestamps of the events.
-
It does not give too much control.
-
If services publish events whenever there is a state change then it can also become a security concern.
-
In this architecture it is difficult to know what the services want to consume
-
If we want some services to consume the events while not allowing others then it makes the event bus more complex.
-
It is difficult to reason about the flow of the system. If we are only looking at the publisher we don't know where the events are going until we look at the event bus.
-
It is difficult to migrate to other architecture.
That's it for now!
You can check out more designs on our video course at InterviewReady.