Microservices Communication Patterns
Master synchronous (gRPC, REST) and asynchronous (event-driven, message queues) communication patterns in distributed systems.
In a monolithic application, services call each other via function calls (in-process). In a microservices architecture, services are deployed independently, forcing us to communicate over the network. This introduces latency, unreliability, and complexity.
Synchronous Communication
REST (HTTP/JSON)
The most common pattern for service-to-service communication.
Pros:
- Simple, human-readable, well-understood.
- Easy to debug (curl, browser, Postman).
- Widely supported tooling.
Cons:
- Text-based (larger payload size).
- Blocking: caller must wait for response.
- Tight coupling: caller must know provider's endpoint.
When to Use: Simple request-response patterns, low-latency internal calls.
gRPC (Protocol Buffers)
High-performance RPC framework using binary serialization.
Pros:
- Binary format (3-7x smaller payloads than JSON).
- Multiplexing over HTTP/2 (single connection for multiple streams).
- Streaming support (server push, bidirectional).
- Strongly typed schema (
.protofiles).
Cons:
- Steeper learning curve.
- Debugging requires tools (not human-readable).
- Requires code generation.
When to Use: High-throughput internal communication, real-time streaming.
// example.proto
service UserService {
rpc GetUser(UserId) returns (User);
rpc ListUsers(Empty) returns (stream User); // Server streaming
rpc CreateUser(stream UserCreate) returns (stream UserCreated); // Bidirectional
}
Asynchronous Communication
Message Queues (RabbitMQ, AWS SQS)
Services publish messages to a queue; consumers pull and process them.
Flow:
Producer -> [Message Queue] -> Consumer
(Persistent Storage)
Pros:
- Decoupled: producer doesn't wait for consumer.
- Resilient: queue persists messages; if consumer crashes, no data loss.
- Load smoothing: queue buffers bursts of traffic.
Cons:
- Delayed processing (polling latency).
- Message ordering issues if multiple consumers.
- Harder to debug (asynchronous failures are subtle).
When to Use: Background jobs, batch processing, high-volume events.
Event Streaming (Kafka)
Unlike queues, Kafka retains messages for a retention period. Multiple consumers can read the same stream independently.
Key Difference:
- Queue: Once consumed, message is removed.
- Stream: Message persists; multiple consumers can replay from any offset.
Use Cases:
- Event sourcing (audit logs, state reconstruction).
- Real-time analytics (streaming pipelines).
- Microservice event broadcasting.
Saga Pattern for Distributed Transactions
When a transaction spans multiple services (no distributed locks), use the Saga Pattern:
Choreography (Event-Driven)
Each service listens to events and publishes new events.
1. Order Service: CreateOrder -> emit "OrderCreated" event
2. Payment Service: Listens to "OrderCreated" -> charges card -> emit "PaymentProcessed"
3. Inventory Service: Listens to "PaymentProcessed" -> updates stock -> emit "InventoryUpdated"
Pros: Loosely coupled, scalable. Cons: Hard to trace (spaghetti logic), difficult to reason about order of events.
Orchestration (Command-Based)
A central orchestrator directs each service's actions.
1. Order Orchestrator -> "Charge Payment" command to Payment Service
2. Payment Service -> charges card, responds success
3. Order Orchestrator -> "Reduce Inventory" command to Inventory Service
4. Inventory Service -> updates stock, responds success
5. Order Orchestrator -> "Confirm Order" to Order Service
Pros: Clear flow, easy to debug, transaction boundaries explicit. Cons: Central orchestrator becomes a bottleneck/single point of failure.
Compensation (Rollback)
If a step fails, execute compensating transactions to roll back previous steps.
Order Created -> Payment Charged -> Inventory Reduced
↓ (Out of stock!)
Rollback: Refund Payment
Rollback: Recreate Order
Consistency Models
Immediate Consistency (Strong)
All services see the same data immediately. Requires distributed consensus (expensive).
Eventual Consistency
Services may temporarily see different views, but converge over time. More scalable but harder to reason about.
Guarantees:
- Read-After-Write: Your own writes are immediately visible.
- Causal Consistency: If B depends on A, A is visible before B.
- Strong Eventual Consistency: Replicas converge quickly (ms to sec).
Choosing a Pattern
| Use Case | Pattern | Reason | |---|---|---| | Simple request-response | REST | Simplicity, low latency | | High-throughput internal | gRPC | Performance, streaming | | Distributed transaction | Saga (Choreography) | Decoupling, scalability | | Audit trail / Analytics | Kafka | Retention, replay, many consumers | | Background jobs | Message Queue | Durability, load smoothing |