Choosing the Best Communication Type for Your Microservices

When you’re designing a microservice architecture, there are a lot of questions you have to answer. Some of them make themselves apparent very early in the process. You need to answer questions about which cloud service to use, and how you’ll orchestrate containers. Other questions don’t show up until a little later in the process, but they’re just as important. Several of those questions revolve around communication between microservices.
While you don’t necessarily need to answer questions about microservice communication early in your microservice journey, you should aim to have solid answers before you start scaling microservices across your organization.
Having a well planned strategy is the most important when you have many microservices and are scaling them across your org. In this article, we’re going to talk about the important facets of microservice communication and help you find a path toward deciding which strategy works best for you.
Communication Between Microservices: Synchronous or Async?
The #1 question you’ll need to answer about your microservice communication style is whether you’ll adopt a synchronous or asynchronous approach. Each approach has its own benefits, so which you’ll want to adopt depends on the role each of your microservices will fulfill. You also don’t need to commit to the same style for every microservice.
You may find that synchronous communication works for some services, while a different approach works for others. When we talk about communication types, it’s important to know that this doesn’t refer to your handler code. You can use a synchronous communication type with async code. What’s key is understanding whether the underlying communication type is synchronous or async.
Each approach has its own benefits, so which you’ll want to adopt depends on the role each of your microservices will fulfill. You also don’t need to commit to the same style for every microservice.
Synchronous Microservice Communication
Choosing a synchronous pattern is very common for a variety of microservices. The most popular protocol for implementing synchronous microservice communication is HTTP. HTTP is a popular choice because servers for handling HTTP requests are widespread in nearly every language, as are libraries for making and decoding HTTP requests. Those libraries make setting up a server and communicating with it very easy. For a lot of teams, that’s all the motivation they need.
Thinking about how we handle HTTP requests from webpages helps to explain how HTTP is a synchronous protocol, regardless of implementation. For instance, Javascript XHR requests work both synchronously and asynchronously. You might write a function that makes a request to a remote server, then uses async/await to cede execution to other parts of the code. But your browser has to sit and wait for that HTTP request to return before it can close the connection. That’s what we mean when we say HTTP is a synchronous protocol.
Pros of Synchronous Communication
Here are some advantages to synchronous communication:
- Protocols are popular and have extremely wide support.
- Synchronous protocols are often easier to debug because the entire communication flow happens in one connection.
- Routing synchronous communication is second nature to many web developers because it is very similar to web server routing.
- Other critical microservice functions, like logging, come integrated or with easy plugins to existing frameworks.
- Because you wait for each communication to finish, it’s easy to make a series of requests sequentially
Cons of Synchronous Communication
While some things are simple with synchronous microservice communication, there are other things that are harder.
- Connecting with a microservice synchronously can carry significant overhead. If you’re making rapid requests that your microservice processes quickly, connection overhead can eat a significant percentage of computation time.
- Synchronous microservice communication protocols are strictly one-to-one communication patterns. You can’t create a HTTP connection or open a WebSocket to multiple servers at the same time. The same is true for more focused server-to-server communication protocols like RPC.
- The server that initiates the request needs to wait for each request to finish. If you make a request that requires a lot of disk I/O or heavy mathematical computation, it’s possible to run into server timeout limits or slow down the service that initiates the request.
- Because of the high overhead, it can be very hard to send a high volume of requests. If you are generating hundreds or thousands of events per second for a microservice, a protocol like HTTP is simply much too slow to send all of those to the service in question.
Async Microservice Communication
The other major type of microservice communication is async, or asynchronous, communication. What’s the big difference? Well, when you adopt a synchronous communication pattern, each time your microservice connects to another service for any reason, that connection stays open until the transaction between services completes.
As we noted above, that means connection times between servers in a synchronous communication paradigm vary widely and can lead to problems like timeouts. Async communication goes in the opposite direction. Instead of opening a connection and waiting for a response, async communication protocols fire off their message and then completely forget about it.
As you might imagine, this comes with some serious pros and some serious cons. We’ll outline those, then we’ll talk about some popular forms of async communication that you might want to research.
Pros of Async Communication
- A “fire and forget” method means the source server can generate thousands of events per second.
- Because connections aren’t one-to-one, you can have a whole swarm of services ready to process new requests.
- It’s easy for service swarms to scale horizontally, meaning they can spin up or spin down hundreds of servers as needed to handle a particular load.
- Testing services in isolation is much easier. The service doesn’t rely on something heavy like a HTTP connection, but instead often starts processing based on nothing more than a data packet. Generating a new data packet is all you need to do to run an independent test.
- Load-testing is also much simpler. It’s very easy to generate a flood of data packets like in our previous point and then observe how the service reacts to a flood of traffic.
Cons of Async Communication
- It is often difficult to debug issues with async communication because it can be hard to trace the flow of a single operation across service boundaries.
- The “swarm” approach to problem-solving means you’ll need more complicated solutions to problems like centralized logging.
- It’s a lot more common for async communication packets to “go missing” and fail to complete the processing chain. Because the originating server doesn’t care if any service picks it up, there’s no guarantee that any service at all processes the message. What’s more, when a message disappears, it’s often difficult to track down why, and where it went.
- Async communication doesn’t guarantee an order. Again, the generation event is “fire and forget.” So if you need to do things in a specific order, async communication requires much more careful data flow logic.
- Async communication usually requires integrating with another system to handle message flow
Async Communication Options
Like we noted, message flow management is an important part of async communication. If this is the way you go, you need a system that ensures your messages get where they’re going. The good news is that this is a mature product space. There are a variety of options—enough that you could fill a whole blog post.
If you’re thinking of adopting an async microservice communication type, here are the big hitters. One common approach is to adopt an event-driven architecture, usually using a system like Apache Kafka. Another option is to go in the direction of a pub/sub architecture, which is often run via an integration with your cloud service provider.
Choose the Right Microservice Communication Type for You
Every system is different. Which communication pattern makes the most sense for your environment depends on your use case. In fact, many environments will adopt elements of both primary types of communication, depending on their specific needs. That makes sense because it’s unlikely that you’d want to adopt an async communication pattern with your database server. So most environments wind up adopting a hybrid model, where the most effective communication pattern is used for each problem.
With that said, my experience is that many microservice environments grow more complex with time. Many organizations adopt more asynchronous patterns as they scale out their services, which complicates the entire environment.
That’s where a tool like OpsLevel’s Service Maturity framework comes in handy. A microservice environment consistently following best practices will stand up well as the environment grows more complicated. If you’re growing your microservice environment, OpsLevel is excited to help you find the right solutions to your problem and ensure your services perform at their best.
This post was written by Eric Boersma. Eric is a software developer and development manager who’s done everything from IT security in pharmaceuticals to writing intelligence software for the US government to building international development teams for non-profits. He loves to talk about the things he’s learned along the way, and he enjoys listening to and learning from others as well.