In previous posts I mentioned ‘microservice communication’ quite often, but never actually explained how exactly that happens.
Because of services isolation, choices of transport are limited and most likely you’ll be using TCP or UDP to talk over the network. But how exactly this will happen depends on what patterns or tools you choose.
Patterns of inter-service communication
There’re two of them: synchronous and asynchronous communication.
Synchronous
Synchronous basically means we’re making a call to another service and immediately start waiting for result. It can be a Remote Procedure Call (RPC), or HTTP request to RESTful service. But in both cases we assume that we’ll get something in response.
This pattern comes very naturally. Good API makes it feel like dealing with local objects and databases. Unfortunately, in distributed apps development this feeling leads to some nasty side effects. Firstly, no matter how we feel, we’re still dealing with networks, and networks fail. Nothing personal, this is just what they do. But in the code we’ll keep pretending this is not true. Maybe add few more try-catch’es and that’s it. Secondly, microservices are supposed to be independent one from another, yet RPC and REST imply some knowledge about the callee. Knowledge brings coupling, and coupling basically is the opposite of independence.
Asynchronous
Asynchronous style of communication is different. Instead of telling another service what to do or how to respond to us, we’ll publish message about something happened, having no idea if there’s somebody listening to it.
Here’s quick example: we have a service with UI for placing an order, and another service for its fulfillment. In sync approach, as soon is user hits “Place Order” button, UI-service would invoke AddOrder method at fulfillment-service side. In async approach, UI-service would publish the message “OrderPlaced, here’s the data. Whatever…” and immediately stopped caring.
Message queue
But we need somewhere to publish messages to and that somewhere is Message Queue. MQ is a a broker, another process, service, or anything else, who receives messages and keeps them for somebody else. There can be additional bells and whistles, but main idea is that simple.
There’s whole variety of MQs to choose from:
- Simple ones, like ZeroMQ, which aren’t brokers and therefore we have to embed them into your services.
- Full blown ones, like RabbitMQ, MSMQ, IBM MQ or Kafka, which scale, support transactions, confirmations of delivery and all sorts of things.
- Cloud solutions, like Amazon SQS, Azure Queue with Service Bus Queue and Google Cloud Pub/Sub.
Having such a variety of tools, it’s not easy to make a choice. But asking right questions might narrow down the list. Some of them could be, for example, should you MQ be durable? In other words, when MQ shuts down with messages in it, should they survive? Cloud MQs, obviously, are inherently durable, unless you nuke the whole data center. Others, like MSMQ support the feature as well, but it’s not something enabled by default. Finally, some, like ZeroMQ aren’t durable.
Then, if message was delivered to the service, but service failed while processing it, should the message get back to the queue? If yes, look for support of transactions and acknowledgments of receiving.
Finally, do you care about messages sent to non-existing MQs, or ones that were unlucky to get to the queue which was already full? If yes, some MQs have so called dead-letter queue (DLQ), created exactly for those unlucky folks.
Messaging patterns
Finally, MQ usually supports more than one usage pattern. For instance:
- Fire-and-forget. It works exactly as it sounds. As soon as the message left the building, we stop caring about it.
- Request-response. It implies some sort of reaction on the message we published, which makes the pattern slightly similar to RPC/REST. We also will need two message queues: one for publishing the message, and one for incoming responses.
- Publish-subscribe. We also can have more than one receiver of a message, and it’s very interesting how MQ makes that possible. Every subscriber will have its own message queue. Publisher sends the message, MQ forwards it to all subscription queues, and they will do the rest.
Conclusion.
Message queues are nice. Often they look cumbersome, yet there’s something appealing in them as well. But as it always happens with the tools, MQs are good in one jobs, suck in others, and come at a price. Use them responsibly and only when you need to. ‘Nice to have’ doesn’t count.
5 thoughts on “Asynchronous communication with message queue”