If your experience is mainly with enterprise applications, working with a system of microservices will require a change in the way you think about communication. The WAR and JAR monolithic deployments of the past allowed you to make assumptions about the availability of objects. In most cases, you were able to treat requests and responses as if they were simply local method invocations. While simple, this pattern obscures the network and pretends that it is reliable.
In distributed systems, you have no assurances that the service you want to invoke will be running, whether a network issue will prevent your request from arriving, or whether the response will ever come. The inherent dynamic nature of distributed systems makes it important to deal with communication failures as normal occurrences. That’s why it makes sense to use messages and realize how they differ from remote method invocation.
Messaging does not need to be point-to-point. Use of messaging can, and often does, mean adopting an event-driven architecture, which can bring additional benefits. Event-driven systems promote autonomy and decoupling, allowing the development organization and the resulting system to scale more easily. They provide good options for managing consistency and persistence.
Messages offer a real world model that allow you to reason logically about requirements. For example, if one of your workmates is away from their desk and you have a question for them, you could leave a note. You don’t know when they might respond: they could be on vacation, or even have left the company. This leaves you a limited number of options:
The desired outcome determines how you handle the message. Effectively, you need to choose between synchronous and asynchronous messaging. In synchronous messaging, a requestor passes a message to another service and expects a timely response, so the requestor waits. This is the familiar pattern often seen in HTTP calls between a client and server.
In contrast, with asynchronous messaging, the requestor simply sends a message and continues with its business. Since microservices depend on the health of their host and network connections, asynchronous messaging offers an obvious advantage. The illustration below illustrates how processing requests asynchronously can speed up execution.
If the message is important, you need some way of persisting it to make sure it will be dealt with at some point in time. An event-driven architecture offers several ways of handling this. For example, in a microservices system, you could use a message broker with delivery guarantees, or write such messages to a database or log. If a reply is required, the sender could just wait for an acknowledgement that the request was received and continue its work, expecting the answer to the question later.
Remembering the events in the system, and allowing for eventual consistency will help you choose the right type of messaging for the job.
“Asynchronous message-passing helps make the constraints—in particular the failure scenarios—of network programming first-class, instead of hiding them behind a leaky abstraction and pretending that they don’t exist…”
In under 15 minutes:
In less than an hour:
Really dig in: