Data Engineering

The Power of Asynchronous Messages in Distributed Systems

Shoham Roditi July 21, 2023 5 min read

In the context of communication between different services or software components, there are two common communication patterns, synchronous and asynchronous communication.

Synchronous messaging is a pattern in which messages or requests are sent between components or systems, and the sender waits for a response before proceeding with its operation. In this context, “synchronous” means that the sender and receiver are synchronized in terms of timing.

Asynchronous messaging is a communication pattern in which messages or requests are sent between different components or systems without requiring an immediate response. In this context, “asynchronous” means that the sender does not have to wait for a response before continuing its operation.

Asynchronous Messaging and why it is important

Asynchronous messaging is a communication pattern for applications to exchange messages without requiring immediate responses. In asynchronous messaging, the sender and receiver operate independently of each other in terms of timing. The sender can send a message or request and continue its work without waiting for a response. The receiver, on the other hand, processes the message or request whenever it becomes available or when it is ready to do so. This decoupling of sender and receiver allows for greater flexibility and scalability in distributed systems.

In order to implement the asynchronous pattern the application creates a producer-consumer pattern. The producer generates the messages towards a middleware component to ensure resilience and fault tolerance. This middleware is usually implemented as a queue, often managed by a message broker. The consumer can then read the message from the message broker without communicating directly with the producer.

This way, the producer gets enough time and ability to attend to another task while the communication remains asynchronous. Asynchronous messaging works by decoupling the sender and receiver of messages. They operate independently and without requiring immediate interaction.

The key aspects of Asynchronous Messaging are:

  • Decoupling sender and receiver, enabling them to operate independently for flexibility, scalability, and fault tolerance in a distributed system.
  • Message queue as a buffer that temporarily stores messages until the receiver can process them and,
  • Workload distribution with parallel processing as the sender-receiver processes messages at its own pace without blocking the sender.

Asynchronous Messages common use cases

Asynchronous messaging is commonly used in various scenarios, including message queues where messages are placed in a queue by the sender and are processed by one or more receivers at their own pace, event-driven systems where events are raised or triggered by different components, and in distributed systems architecture.Asynchronous Messages Common Usecase

Common use cases for asynchronous communication include:

  • E-commerce order processing asynchronous messaging for orders. If a customer places an order, its details are sent as an asynchronous message to the order processing system. This means the system can still handle the order at its own pace while processing payment without blocking the customer’s interaction with the app.
  • Notifications and Alerts use Asynchronous messaging to send users notifications and alerts. This is a common generation of social media applications. Asynchronous messaging handles events such as notifications for new messages. In this case, the messages are queued and delivered to users when they are online or available to receive notifications.
  • Background Processing uses asynchronous messaging to offload resource-intensive tasks to background workers. In such a case, the messages representing tasks will be saved to a queue where service workers retrieve them asynchronously once done. Such processing means the main application thread will remain responsive while the tasks are executed in the background.

Asynchronous vs Synchronous communication

For any application communication to take place, it requires the sender and receiver. At this point, we can describe the main differences between synchronous and asynchronous message patterns:

In synchronous massaging pattern, the sender and the receiver are aware of each other, the communication is strict between the two parties, and the sender and receiver must be present as the messages are being processed immediately.

In an asynchronous massaging pattern, on the other hand, the sender does not aware of the receiver’s condition and even how many receivers there are, it is only guaranteed that the message will be processed at some point in the future.  This part of the system’s architecture is derived from the communication pattern, and as mentioned before, asynchronous communication will be usually supported by a Message Broker.

Asynchronous messaging offers advantages such as:

  • It ensures message persistence and history. The message flow is regulated using message brokers. It saves the messages for participants to retrieve them.
  • Asynchronous messaging is global and not limited to time zones.
  • Flexibility to respond to messages at convenience. Consumers engage with the message and respond when available.
  • It’s built for resilience and scalability. All the components (producers, consumers, broker) are separated, and you can scale each of them when independently.
  • Decouples your application in mini services to improve performance and throughput.
  • Offline support as messages are stored in a queue and persisted until the recipient becomes available.

On the other hand of the spectrum, asynchronous messaging introduces disadvantages such as:

  • You need additional components like message brokers to create a complete async pattern. In addition, you must have the expertise needed to use message brokers.
  • Messages are stored in a queue. This introduces potential points of failure, such as ensuring messages were delivered to intended receivers, message duplication, and failed message deliveries.
  • To implement an asynchronous messaging design, you need timeouts, message loss, retries, and failed processing attempts concepts that increase development complexity.

From the advantages and disadvantages we can learn when it is recommended to use each pattern:

  1. Complexity and Coupling: Asynchronous communication can introduce more complexity, as you need to handle out-of-order responses or deal with potential message queues. Synchronous communication is simpler to implement and understand. Asynchronous communication is more suitable when you want to decouple components and allow them to work independently.
  2. Response Time: If you need immediate responses or real-time interactions, synchronous communication is more appropriate. Asynchronous communication is better when you can tolerate some delay or when responses are not time-sensitive.
  3. Resource Utilization: Synchronous communication may be more resource-intensive since it requires both sender and receiver to be active simultaneously. Asynchronous communication allows for better resource utilization as the sender can continue its work while waiting for a response.
  4. Error Handling: In synchronous communication, errors are typically handled immediately since the sender is blocked until a response is received. In asynchronous communication, errors may be handled asynchronously, and retries or error-handling mechanisms might be necessary.

Asynchronous messaging best practices

There are best practices to take under consideration when implementing asynchronous messaging pattern:

  • Before implementing your asynchronous messaging application, ensure a reliable message broker meets your specific requirements, such as message persistence, high availability, and fault tolerance. 
  • Creating content handles message failures. Your state should be able to carry out retries for any messages that have a failed delivery.
  • Consider batching and bulk processing for related messages while mindful of latency trade-offs and throughput.
  • Create idempotent producers. Here you need to handle deduplication mechanisms, add unique message identifiers, and use idempotent delivery protocols to create an At-Least-Once delivery pattern.
  • Ensure message ordering and delivery semantics for your messages. Message payloads should have timestampping to track messages’ orders.