Asynchronous Message-Based Communication

  • By Hasitha Abeykoon
  • 5 Dec, 2019

Acronyms

SMS - Short Message Service

JMS - Java message service

AMQP - Advanced Message Queuing Protocol

HTTP - HyperText Transfer Protocol

JNDI - Java Naming Directory Interface

Amazon SQS - Simple Queue Service

HA - High Availability

What is asynchronous messaging?

Asynchronous messaging is a communication method, where the sending party can send a message and continue with its unrelated tasks without waiting for an immediate response from the other party. This eliminates the need for both communicating parties to be up and running at the time of communication. Usually, a message is placed at a third-party communication entity so that the interested subscribers can receive the message. This makes the message producer completely independent of message consumers. It does not need to know who the consumers are, whether they are active at the moment of communication, or their addresses. It just needs to know how to reach the third-party entity to store the messages. 

This third-party communication entity is known as a “Message Broker”.

Even if we do not recognize it by this formal name, we use asynchronous messaging in day-to-day life — examples include SMS, email, and online chat applications such as Facebook, Whatsapp, and Viber.

Why asynchronous messaging?

There are many use cases for asynchronous messaging. One primary advantage is that the message producer does not need to wait until a response comes back. It can continue with other tasks. For example, if you are making a smartphone application, this enhances the user experience as a user can work on something else in the application. Sometimes, servers that process requests can take some time, maybe because they need to call a few other backend servers to gather relevant data. When the server is ready with the response, it will inform the broker, so that the application will receive the message as a notification (event).

As communication systems have become more complex and popular, users now need to communicate even more reliably. Sometimes, web protocols such as HTTP/HTTPS do not provide the required resilience and reliability. With the advent of protocols such as AMQP, systems can be designed to receive an acknowledgment for the message they send and send an acknowledgment back for the message they receive and process successfully. This is another useful aspect of asynchronous messaging as it helps to improve the quality of communication.

Despite the fact that the primary goal of asynchronous messaging is improving performance and reliability, there are a number of other use-cases. In this article, we will discuss some popular ones.

Single receiver-based communication (message-based commands)

Single receiver-based asynchronous communication refers to point-to-point asynchronous communication between the message sender and the receiver. In this messaging paradigm, the message is sent from the publisher only once to the broker, and it is delivered to the targeted single consumer only once. If the consumer acknowledges the message, it will be immediately removed from the broker. There can be a retry mechanism if the receiver does not acknowledge the message to the broker. In that case, the processing logic of the message at the consumer side should be idempotent, so that it can handle a re-delivery, or there should be necessary mechanisms to identify re-delivered messages.

Typically, a queue is used to implement a point-to-point messaging pattern.

Figure 1: A point-to-point messaging pattern

Messages are published to the queue on the broker and they are received by the consumer in the same order they are published to the queue. This is a suitable way to send commands from one software component to the other in an asynchronous manner (i.e., between microservices). The command-receiving component can process the command and change its internal sub-components accordingly. This makes the two components scalable and independent.

Nevertheless, we should be ready for failures. If the receiver connection is dropped, the receiving component should know how to recover it. Otherwise, the receiver will not receive messages from the publisher.

Multiple receiver-based communication (event-driven communication)

The messaging paradigm with multiple receivers is known as publish/subscribe. There is more than one subscriber for a message that is sent by the message producer. They all receive a copy of the same message so that they can process them individually. The publisher does not know about the subscribers, and, at the same time, subscribers are not aware of each other. This makes the architecture scalable as per the open/closed principle. Additional subscribers can be added without modifying the sender or any other subscriber. Usually, a “topic” is used to implement a publish-subscribe messaging pattern. Sometimes, this is commonly referred to as an “event bus”.

Figure 2: Multiple receiver-based communication

When registering for events (i.e., creating a subscriber), modern messaging platforms allow users to register using wildcards, so that depending on the event, the metadata or hierarchy of event domain messages will be received. These mechanisms provide a flexible way to consume events that interest a subscriber.

If a system is designed using asynchronous events, it is important that the consumer application understands that the system will only guarantee eventual consistency. The application cannot expect immediate changes in the system or immediate results. 

Resiliency in asynchronous communication

One of the major challenges in asynchronous communication is preventing message loss and guaranteeing that messages are delivered. There are mechanisms built into messaging protocols, such as AMQP, to facilitate this; nevertheless, system designers should be aware of these.

Message acknowledgment

Providing acknowledgments to the received messages is a primary way of handling resiliency in asynchronous messaging. To do this, protocols and messaging APIs provide the following constructs.

Figure 3: Message acknowledgement

Protocol level support

In AMQP [1], there are two frames involved in communicating back to the message broker. Quoting 0-91 specification:

  • ack — This method acknowledges one or more messages delivered via the Deliver or Get­Ok methods. The client can ask to confirm a single message or a set of messages up to and including a specific message.
  • reject — This method allows a client to reject a message. It can be used to interrupt and cancel large incoming messages, or return untreatable messages to their original queue.

Java JMS API support

In the JMS API provided by Java [2], users can specify the acknowledgment pattern required. Depending on the acknowledgment patterns, different delivery guarantees can be achieved.

  • AUTO_ACKNOWLEDGE — This automatically acknowledges the message as soon as it is received to the JMS application.
  • CLIENT_ACKNOWLEDGE — This controls the message acknowledgment inside the application. A user can programmatically acknowledge the received message whenever needed (i.e., if the processing is successful)
  • DUPS_OK_ACKNOWLEDGE— This lazily acknowledges the delivery of messages.The JMS application should be able to tolerate duplicates if they are sent by the broker.

For more information, please refer [3] by Oracle and [4] for different acknowledgment patterns.

Transactional message sending and receiving

Messaging protocols like AMQP support transactions. In JMS API, users can make the producer session transactional and send the messages to the message broker. The messages will be available in the broker only after committing the session.

Figure 4: Transactional message flow

In the same manner, users can make consumer sessions transactional and receive messages from the broker. After receiving the messages, if the consumer session is committed by the receiving client, the broker will consider the messages that are successfully received by the consumer and remove them from the broker. If the session is rollbacked, the broker will consider message delivery to the client as unsuccessful and the messages will remain in the broker to be redelivered again. Modern messaging platforms provide support for distributed transactions as well.

System-level messaging patterns

There are some architectural patterns that can be used to achieve resilience in message delivery. 

  • Using an event sourcing pattern [5] to keep track of events that made changes or that we can replay and recover
  • Using the outbox pattern [6] to save and forward messages, so that in case of failure, messages can be resent

However, it is not a trivial task to achieve once-and-only-once guaranteed delivery, especially in a distributed messaging system. Many systems are satisfied with at-least-once delivery or once-or-more delivery.

Communication between systems working with different message rates

If we consider two different messaging systems, they may have different stress levels for message processing. One may tolerate higher message rates but the other may fail on the same message rate. Some systems perform in-memory message processing, while others have to deal with create, read, update, and delete (CRUD) operations with databases and talk to other systems to fetch relevant data for processing.

When you design a compound service on top of existing services that have different stress levels for message rates, you need to be aware of what is the maximum messaging rate each service can tolerate. You can use asynchronous messaging between such systems so that services that cannot handle high loads during peak hours are saved. In that way, a compound service is not affected by the slowness of a particular service at peak time.

Figure 5: Working with different message rates

Note: This is not a solution if the incoming traffic rate by System A is consistently higher than the load System B can handle. If we use asynchronous messaging for such a case, the message queue at the intermediate broker will continue to grow and the time taken to forward a particular stored request will increase.

Making HTTP calls asynchronous

HTTP is a synchronous protocol, which means both the sender and the receiver (web service) should be online and the service has to respond immediately. If a particular backend service takes a considerable amount of time to respond and the caller does not need to wait, how can we make use of asynchronous messaging in such a situation?

The inbound message will be an HTTP message. The outbound message will be a JMS message over AMQP or an RabbitMQ message to a RabbitMQ broker. There is a protocol conversion to be done. We need an integration tool to perform the protocol conversion and publish the message to the broker or event bus. At the same time, we need to respond back to the HTTP caller that the message is accepted for processing. Usually, in an HTTP world, this is done by sending back an HTTP 202 response to the caller.

Then, the backend service should be able to receive the message listening to the event bus/broker and process the message. As it is a web service, again, an integration tool should listen to the broker/event bus, receive the messages, do the protocol conversion back to HTTP and forward the messages to the web service. This integration pattern is sometimes called “Store and Forward”.

Figure 6: Store-and-forward pattern

Integration of synchronous and asynchronous systems

Consider an incoming HTTP message that expects an immediate synchronous response, but to serve the request you need to forward it to an external backend system, which is maintained by a different vendor. That backend service will listen to a queue on an event bus, receive messages, process them, and place the response back to another queue defined in the message itself (usually we call this a “reply queue”).

To perform this integration, you need an integration tool that supports a “dual channel messaging pattern”. An integration tool will convert incoming HTTP message and publish to the event bus. The same integration layer will listen to the reply queue specified, get the response, and convert it back to HTTP as the response for the inbound HTTP call. Please refer to the image below.

Figure 7: Dual channel messaging pattern

As discussed above (i.e., Making HTTP calls asynchronous), we need an integration tool to perform the protocol conversion and integrate different systems in an asynchronous manner. WSO2 Enterprise Integrator is one such integration tool that gives developers a configuration-driven approach to implement asynchronous messaging patterns. In the next article, we will discuss its capabilities.

Conclusion

In this article, we discussed what asynchronous messaging is and what its use cases are. It is a popular messaging pattern not only within middleware systems but also at edge client systems. Over time, users have invented protocols and standards to perform asynchronous messaging so that different systems can be integrated together. Moreover, vendors now provide integration software with plug-and-pay and configuration-driven approaches to implement asynchronous messaging patterns. 

References

[1]. http://www.amqp.org/specification/0-9-1/amqp-org-download

[2]. https://docs.oracle.com/javaee/1.3/jms/tutorial/1_3_1-fcs/doc/overview.html#1100542

[3]. https://docs.oracle.com/javaee/1.3/jms/tutorial/1_3_1-fcs/doc/advanced.html#1025869

[4]. https://wso2.com/library/articles/2013/01/jms-message-delivery-reliability-acknowledgement-patterns/

[5]. https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing

[6]. http://www.kamilgrzybek.com/design/the-outbox-pattern/

About Author

  • Hasitha Abeykoon
  • Associate Technical Lead
  • WSO2
Hasitha is an Associate Technical Lead at WSO2. He has been part of the Message Broker team since he joined in December 2011. He holds a B.Sc. in Computer Science & Engineering from the Department of Computer Science and Engineering of University of Moratuwa, Sri Lanka. Hasitha has been a consultant for several customers on behalf of WSO2, for products such as WSO2 ESB, WSO2 Message Broker, and WSO2 Data Services Server. He focuses his research and development in asynchronous messaging, messaging reliability, NoSQL databases, and big data concepts for performance.