Problem
How do services in a microservice architecture communicate?
Solution
Use asynchronous messaging for inter-service communication. Services communicate by exchanging messages over messaging channels.
There are several different styles of asynchronous communication:
- Request/response - a service sends a request message to a recipient and expects to receive a reply message promptly
- Notifications - a sender sends a message a recipient but does not expect a reply. Nor is one sent.
- Request/asynchronous response - a service sends a request message to a recipient and expects to receive a reply message eventually
- Publish/subscribe - a service publishes a message to zero or more recipients
- Publish/asynchronous response - a service publishes a request to one or recipients, some of whom send back a reply
Examples
There are numerous examples of asynchronous messaging technologies
public class OrderService {
...
public Order createOrder(long consumerId, long restaurantId,
List<MenuItemIdAndQuantity> lineItems) {
Restaurant restaurant = restaurantRepository.findById(restaurantId)
.orElseThrow(() -> new RestaurantNotFoundException(restaurantId));
List<OrderLineItem> orderLineItems = makeOrderLineItems(lineItems, restaurant);
ResultWithDomainEvents<Order, OrderDomainEvent> orderAndEvents =
Order.createOrder(consumerId, restaurant, orderLineItems);
Order order = orderAndEvents.result;
orderRepository.save(order);
orderAggregateEventPublisher.publish(order, orderAndEvents.events);
OrderDetails orderDetails = new OrderDetails(consumerId, restaurantId, orderLineItems, order.getOrderTotal());
CreateOrderSagaState data = new CreateOrderSagaState(order.getId(), orderDetails);
createOrderSagaManager.create(data, Order.class, order.getId());
meterRegistry.ifPresent(mr -> mr.counter("placed_orders").increment());
return order;
}
Messaging
In this approach, the producer will send the messages to a message
broker and the consumer can listen to the message broker to receive
the message and process it accordingly. There are two patterns
within messaging: one-to-one and one-to-many.
We talked about some of the complexity synchronous styles brings,
but some of it is eliminated by default in the messaging style. For
example, service discovery becomes irrelevant as the consumer and
producer both talk only to the message broker. Load balancing is
handled by scaling up the messaging system.
Failure handling is in-built, mostly by the message broker.
RabbitMQ, ActiveMQ, and Kafka are the best-known solutions in cloud
platforms for messaging.
In this approach, the producer will send the messages to a message broker and the consumer can listen to the message broker to receive the message and process it accordingly. There are two patterns within messaging: one-to-one and one-to-many.
We talked about some of the complexity synchronous styles brings, but some of it is eliminated by default in the messaging style. For example, service discovery becomes irrelevant as the consumer and producer both talk only to the message broker. Load balancing is handled by scaling up the messaging system.
Failure handling is in-built, mostly by the message broker. RabbitMQ, ActiveMQ, and Kafka are the best-known solutions in cloud platforms for messaging.
Event-Driven
The event-driven
method looks similar to messaging, but it serves a different purpose.
Instead of sending messages, it will send event details to the message
broker along with the payload.
Consumers will identify what the event is and how to react to it. This
enables more loose coupling. There are different types of payloads
that can be passed:
-
Full payload — This will have all the data related to the event
required by the consumer to take further action. However, this makes
it more tightly coupled.
-
Resource URL — This will be just a URL to a resource that represents
the event.
-
Only event — No payload will be sent. The consumer will know based
on on the event name how to retrieve relevant data from other
sources, like databases or queues.
There are other styles, like the choreography style, but I personally
don't like that. It is too complicated to be implemented. This can
only be done with the synchronous style.
That's all for this blog. Let me know your experience with
microservice-to-microservice communication.
The event-driven method looks similar to messaging, but it serves a different purpose. Instead of sending messages, it will send event details to the message broker along with the payload.
Consumers will identify what the event is and how to react to it. This enables more loose coupling. There are different types of payloads that can be passed:
- Full payload — This will have all the data related to the event required by the consumer to take further action. However, this makes it more tightly coupled.
- Resource URL — This will be just a URL to a resource that represents the event.
- Only event — No payload will be sent. The consumer will know based on on the event name how to retrieve relevant data from other sources, like databases or queues.
There are other styles, like the choreography style, but I personally don't like that. It is too complicated to be implemented. This can only be done with the synchronous style.
That's all for this blog. Let me know your experience with microservice-to-microservice communication.
Resulting context
This pattern has the following benefits:
- Loose runtime coupling since it decouples the message sender from the consumer
- Improved availability since the message broker buffers messages until the consumer is able to process them
- Supports a variety of communication patterns including request/reply, notifications, request/async response, publish/subscribe, publish/async response, etc
- The additional complexity of message broker, which must be highly available
- Request/reply-style communication is more complex
This pattern has the following drawbacks:
This pattern has the following issues: