0
0
Microservicessystem_design~7 mins

Request-response vs event-driven in Microservices - Architecture Trade-offs

Choose your learning style9 modes available
Problem Statement
When services communicate synchronously using request-response, a slow or failing service can block the entire workflow, causing delays or failures. Conversely, without asynchronous communication, systems struggle to handle high loads or complex workflows that require decoupling and resilience.
Solution
Request-response uses direct calls where a service waits for a reply before continuing, ensuring immediate feedback but tight coupling. Event-driven architecture sends messages or events asynchronously, allowing services to react independently and improving scalability and fault tolerance by decoupling components.
Architecture
Service A
Service A
Service B
Service B

The diagram shows two communication styles: synchronous request-response where Service A waits for Service B's reply, and asynchronous event-driven where Service A emits an event to an event bus that Service B consumes independently.

Trade-offs
✓ Pros
Request-response provides immediate feedback and simpler error handling due to synchronous calls.
Event-driven enables loose coupling, improving system scalability and resilience to failures.
Event-driven supports complex workflows and better load distribution by decoupling services.
✗ Cons
Request-response can cause cascading failures and increased latency if downstream services are slow or unavailable.
Event-driven systems add complexity in managing eventual consistency and debugging asynchronous flows.
Event-driven requires infrastructure like message brokers, increasing operational overhead.
Use request-response when immediate response is critical and service dependencies are stable and low-latency. Use event-driven when building scalable, resilient systems with complex workflows or high throughput requirements.
Avoid request-response for high-latency or unreliable downstream services to prevent blocking. Avoid event-driven for simple, low-scale systems where added complexity and infrastructure are not justified.
Real World Examples
Uber
Uses event-driven architecture to decouple services like ride matching and notifications, enabling asynchronous processing and scaling under high demand.
Netflix
Employs request-response for user authentication where immediate validation is required, and event-driven for streaming data processing pipelines.
Amazon
Uses request-response for order placement to ensure immediate confirmation, and event-driven for inventory updates and shipment tracking asynchronously.
Code Example
The before code shows a synchronous HTTP request where Service A waits for Service B's response. The after code demonstrates an event-driven approach where Service A publishes an event to an event bus, and Service B asynchronously processes it without blocking Service A.
Microservices
### Before: Request-Response (Synchronous)
import requests

def get_user_data(user_id):
    response = requests.get(f"http://service-b/users/{user_id}")
    if response.status_code == 200:
        return response.json()
    else:
        return None


### After: Event-Driven (Asynchronous)
import asyncio

class EventBus:
    def __init__(self):
        self.subscribers = []

    def subscribe(self, callback):
        self.subscribers.append(callback)

    async def publish(self, event):
        for subscriber in self.subscribers:
            await subscriber(event)

async def service_a(event_bus, user_id):
    event = {"type": "UserRequested", "user_id": user_id}
    await event_bus.publish(event)

async def service_b(event):
    if event["type"] == "UserRequested":
        print(f"Processing user {event['user_id']}")

async def main():
    bus = EventBus()
    bus.subscribe(service_b)
    await service_a(bus, 123)

asyncio.run(main())
OutputSuccess
Alternatives
Batch Processing
Processes data in large groups at scheduled times rather than real-time communication.
Use when: Choose when real-time interaction is not required and throughput optimization is prioritized.
Polling
Clients repeatedly check for updates instead of receiving events asynchronously.
Use when: Choose when event-driven infrastructure is unavailable and update frequency is low.
Summary
Request-response communication waits for immediate replies, making it simple but tightly coupled and potentially blocking.
Event-driven communication uses asynchronous events to decouple services, improving scalability and fault tolerance.
Choosing between them depends on system requirements like latency sensitivity, complexity, and scale.