microservices antipatterns and pitfalls - Give It a Rest Pitfall
Abstract
Using REST for all communication between services is a pitfall as it’s limited in its capabilities. There’s another way to communicate, using e.g. AMQP standard:
- asynchronous requests
- broadcast
- transacted requests
Using API REST API is by far the most popular choice for accessing microservices and communicating between services. It’s so common a choice that most of the popular template frameworks (e.g., DropWizard, Spring Boot, etc.) have REST access already built into the service templates. If REST is such a popular choice, then why is it a pitfall? The give it a rest pitfall is about using REST as the only communication protocol and ignoring the power of messaging to enhance your microservices architecture. For example, in a RESTful microservices architecture, how would you handle asynchronous communications? What about the need for broadcast capabilities? What do you do if you need to manage multiple remote RESTful calls within a transactional unit of work?
There are two types of messaging standards you should be aware of when considering using messaging for your microservices architecture—platform-specific standards and platform-independent standards. Platform-specific standards include the JMS for the Java platform and MSMQ for the .NET platform. Both describe a standard API used within the platform, independent of the messaging provider (vendor) you are using. For example, in the Java platform you can swap out brokers (e.g., ActiveMQ, HornetQ, etc.) with no API changes. While the API is standard and remains the same, it’s the underlying proprietary protocol between these brokers that is different (which is why you need to have the same client JAR and server JAR for the same vendor). With platform-standard messaging protocols you are more concerned about portability via a common API rather than the actual vendor product you are using or the wire-level protocols used.
The current platform-independent standard is AMQP. AMQP, which standardizes on the wire-level protocol, not the API. This allows heterogeneous platforms to communicate with one another, regardless of the vendor product you are using. A client using RabbitMQ, for example, can easily communicate with a StormMQ server (assuming they are using the same protocol version). AMQP using RabbitMQ is currently the most popular choice for messaging within a microservices architecture, mostly because of its platform-independent nature.
Asynchronous Requests
The first consideration for using messaging within your microservices architecture is asynchronous communication. With asynchronous requests the service caller does not need to wait for a response from the service when making a request, as illustrated in Figure 10-1. This is sometimes referred to as “fire-and-forget” processing.
Figure 10-1. Asynchronous communications using messaging
Not only does asynchronous processing increase overall performance, but it also adds an element of reliability to your system. Performance is increased because callers don’t have to wait for a response if none is needed. Through guaranteed delivery, the message broker ensures that the service will eventually receive the message. Reliability is increased because the caller doesn’t need to worry about setting timeout values or using the circuit breaker pattern when communicating with a service (see The Timeout AntiPattern).
Broadcast Capabilities
Another very powerful feature of messaging that is not available within REST is the capability to broadcast a message to multiple services. This is known in messaging as “publish-and-subscribe” messaging, and usually involves topics and subscribers (depending on the messaging standard you are using). Figure 10-2 illustrates the basic behavior of broadcast messaging.
Figure 10-2. Broadcast capabilities using messaging
Broadcast messaging involves a message producer sending out the same message to multiple message receivers (i.e., services). The message producer generally doesn’t know who is accepting the message or what they are going to do with it. For example, a message producer may broadcast a message informing consumers about a stock split for Apple stock (AAPL). The message producer only has the responsibility of publishing a message to a topic (JMS), a fanout or topic exchange (AMQP), or a multicast queue (MSMQ). The stock split message may be picked up by any number of consumers, or no consumers at all.
Transacted Requests
Messaging systems support the notion of transacted messages, meaning that if messages are sent to multiple queues or topics within the context of a transaction, the messages are not actually received by the services until the sender does a commit on that transaction. The service consumer sends a message to the first service and then sends another message to the second service, as illustrated in Figure 10-3. Until the service consumer performs a commit, those messages are held in the queues. Once the service consumer performs a commit, both messages are then released.
Figure 10-3. Transaction capabilities of messaging
If the service consumer in Figure 10-3 sends a message to the first queue, but then experiences some sort of error, the service consumer can perform a rollback on the messaging transaction, which would effectively remove the message from the first queue.
Implementing this sort of transaction capability using REST would be very difficult, essentially requiring the service consumer to issue compensating requests to reverse the updates made by each request. Therefore, it is a good idea to consider using transacted messaging any time a service consumer needs to orchestrate multiple remote requests.