Message Exchange Patterns (MEPs)

An overview of the most common Message Exchange Patterns (MEPs) applicable to the Business Integration and SOA domains.

Posted on February 1, 2013 by Ernesto Garbarino

Overview

Message Exchange Patterns (MEPs) describe how systems may intercommunicate using unanimously understood mechanisms. Unless otherwise specified, MEPs describe logical patterns rather than strict physical ones:

This is a summary of the most common patterns:

Logical MEP Synonymous and common descriptions
Request/Response Request/Reply, In-Out, RPC
One-Way Fire and Forget, In-Only, Queue-based
Publish/Subscribe Pub/Sub, Topic Queue, Subscription-based
Request/Call-Back Just “call-back”

Request/Response

The Request/Response Message Exchange Pattern (MEP) is that in which the consumer sends a request and waits for a response. This pattern may also be referred to as Request/Reply, In-Out (SOAP), and RPC.

A Request/Response MEP may be implemented in a synchronous, asynchronous, or polling fashion:

  1. Synchronous Request/Response MEP: the consumer has to wait until the provider is able to produce a response or keep track of a communication stream.
  2. Asynchronous Request/Response MEP: the consumer’s control flow is released right after issuing the request. The provider will produce the request in parallel and hand it over to the consumer once it is ready.
  3. Polling Request/Response MEP: similarly to the previous MEP, the consumer’s code control flow is released right after issuing the request since the provider will produce the response asynchronously. However, the consumer has to check repeatedly (poll) the provider until the response becomes available.

Synchronous Request/Response MEP

Unless otherwise specified, the Request/Response MEP is assumed to be synchronous. This means that the consumer is blocked (it cannot resume its code flow) until the provider is able to produce a response.

In regular (non event-based) programming languages, all regular method/function invocations are, by definition, synchronous. The invoking code cannot continue unless the method or function returns a result.

A key aspect of the Synchronous Request/Response MEP is that both the consumer and the provider must be on-line until the response is fully streamed back to the consumer. Any interruption in between may result in the transaction’s data being lost forever. This is the nature of most HTTP-based communication and the reason why the concept of idempotency is so relevant in architectural styles such as REST.

An invocation to a synchronous API may be single-threaded blocking, single-threaded non-blocking or multi-threaded.

Blocking Single-threaded Invocation

This is the default invocation mode in which the consumer’s flow remains blocked until a response is returned. Unless the I/O driver supports an effective timeout mechanism, the consumer’s code becomes subordinate to the provider’s latency as well as to that of its supply chain.

Pseudocode example:

1. rate = provider.getExchangeRate(1.2, "USD","GBP")
2. ... flow is stuck here ...
3. print "The exchange rate is : " + rate

Non-blocking Single-threaded Invocation

In the case of a non-blocking invocation, the consumer may check the availability of response-data interactively and fetch data in chunks. This provides the following advantages:

Pseudocode example:

1. request = provider.getExchangeRate(1.2,"USD",GBP")
2. while (!request.sendEOF)
3.    print "Sending request..."
4. end while
5. while (!request.receiveEOF)
6.    print "Receiving response...";
7. end while
8. print "The exchange rate is : " + request.response;

Multi-threaded Invocation

A multi-threaded invocation has the same advantages as the non-blocking one, except that the consumer code does not need to interact with the request object or functions to keep track of its progress. In this case, the invocation code runs as a parallel, independent process.

Pseudocode example:

1. define requestThread as Thread
2.    rate = provider.getExchangeRate(1.2, "USD","GBP")
3.    print "The exchange rate is : " + rate
4. end define
5.
6. run requestThread
7. ... continue flow  ...

Asynchronous Request/Response MEP

The asynchronous request/response MEP is an approach to create an external, logical request/response logical flow that is, in reality, implemented as a request/call-back interaction. This pattern may be referred to as “virtual synchronous” or “synch to asynch wrapper”.

This pattern is typical in the construction of web services that wrap legacy queue-based systems such as those based on IBM WebSphere MQ technology.

The main advantage of this approach is that the process of sending and receiving a request are decoupled. This allows the consumer to perform other activities in-between the message exchange.

The disadvantage is that a correlation identifier may be necessary and that the provider may require reliability features (such as message storage and fail/retry mechanisms) since the consumer may no longer be on-line when the provider has finished calculating the response.

Pseudocode example: no correlation identifiers are shown for simplicity.

 1. define onMessage (response)
 2.   rate = response    
 3. end define
 4. 
 5. provider.request.getExchangeRate(1.2, "USD","GBP")
 6. provider.callBack = onMessage    
 7. provider.invoke()
 8.
 9. while (rate == null) 
10.   ... do something else ...
11. end while
12.
13. print "The exchange rate is : " + rate

Explanation:

Polling Request/Response MEP

This pattern could be seen as a two–step process in which the first step consists of the One-Way MEP, and the second one of a regular Synchronous Request/Response MEP.

Unlike the asynchronous Request/Response MEP, the provider is unable to start a request towards the provider; instead, the consumer has to poll the provider regularly until a response becomes available. The use of a correlation identifier is almost mandatory since the consumer has to tell the provider the specific request in which it is interested.

Pseudocode example:

1. id = provider.requestExchangeRate(1.2, "USD","GBP")
2.    
3. while (rate == null) 
4.   rate = provider.requestExchangeResultForId(id)
5. end while

Request/Response in SOAP

This is how the Request/Response MEP is understood in SOAP:

SOAP 1.0 SOAP 1.2 Description
Request/Response In-Out Conventional MEP
Solicit-Response Out-In The provider becomes the consumer and vice versa
n/a In-Optional-Out Optional response
n/a Out-Optional-In Optional response (the provider becomes the consumer)

One-Way

The One-Way Message Exchange Pattern (MEP) is that in which the consumer sends a one-off message to a provider with no expectation of a response. This pattern may also be referred to as Fire and Forget, In-Only (SOAP), and sometimes Queue based.

A One-way MEP may be implemented in asynchronous, asynchronous/verifiable or synchronous/reliable fashion:

Asynchronous One-Way MEP

This is the “pure” fire and forget implementation of the pattern in which the consumer has no means to verify message arrival. The pattern is rarely implemented in such a crude fashion unless:

  1. Only “statistical arrival” is important; for example, a requirement that says that only 80% of messages must arrive. Even so, a monitoring mechanism must still be in place to keep track of statistical data.
  2. The message is delivered through a safe mechanism which “doesn’t drop messages”. In this case there is a form of middleware in place which acts as the actual consumer. Such a middleware layer would be, in reality, following the Asynchronous/Verifiable MEP.

Pseudocode example:

1. provider.submitExpenses("Slavoj Zizek", 4.50, "Pizza");
2. ... flow continues ...

Asynchronous One-Way/Verifiable MEP

This variation still has the advantage of the fire and forget aspect in the sense that the consumer does not need to wait for the provider for an acknowledgement. However, at some point in the future, the provider will contact the consumer to notify of the messages it has received.

This scheme permits the consumer to run a “reconciliation process” and send back those messages that have not been received by the provider.

Pseudocode example:

 1. define acknowledge (name) 
 2.   unverified.remove (name)
 3. end define

 4. define retryUnverified as Thread
 5.   while (unverified.size > 0)
 6.      for all (name : unverified)
 7.         provider.invoke (name)
 8.      end for all          
 9.   end while
10. end define
11.
12. name = "Zizek" 
13. provider.register (
14.    name, 
15.    submitExpenses("Zizek", 4.50, "Pizza")
16. )
17.   
18. unverified.add (name)
19. provider.invoke (name)
20.  
21. ... do more ...
22. ... receive call backs to acknowledge ...
23. 
24. run retryUnverified

Explanation:

Notes:

Synchronous One-Way/Reliable MEP

This is a logical One-way MEP which is implemented as a physical Request/Response MEP with the difference that the Response is only used to confirm (acknowledge) the message arrival and/or report a fault with it. The Response is not intended to carry “business data”.

Most HTTP-based One-way MEP implementations are, in fact, of this variety. A developer would need to disregard the HTTP specification to make either the provider or the consumer behave in a true fire and forget manner.

Let’s take a regular HTTP web service call as an example:

Request:

POST /ws/get HTTP/1.1
Accept: application/json
Content-Length: xxx
Content-Type: application/json
Host: ws.tesira.com

{ 
  "name" : "Zizek",
  "amount": 4.5,
  "description": "Pizza"
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxxx

The response, as such, is part of the HTTP specification and serves as a default acknowledgement device unless the developer decides to forgo it.

Other Classifications

The discussed variations discriminate the pattern based on how—if at all—message arrival is acknowledged. Alternatively, the One-Way MEP may be classified based on the number of recipients [2005-erl-soa]:

One-Way in SOAP

This is how the One-Way MEP is understood in SOAP:

SOAP 1.0 SOAP 1.2 Description
One-Way In-Only Conventional MEP
Notification Out-Only The provider becomes the consumer and vice versa
n/a Robust-In-Only Similar to Synchronous/Reliable One-Way (it may produce a fault)
n/a Robust-Out-Only Same as above with consumer/provider roles reversed

Publish/Subscribe

The Publish/Subscribe Message Exchange Pattern (MEP) is that in which consumers subscribe with providers to receive notifications. This pattern may also be referred to as Pub/Sub, Topic-based, and Subscription-based.

This pattern assumes the following roles and entities:

Publish/Subscribe is typically implemented using the One-Way MEP in a bidirectional fashion; the consumer sends subscription messages to the provider and the provider sends topic-specific messages to the consumer. This pattern is similar to the Observer OOP pattern.

Pseudocode example:

define onMessage (message)
   if message.topic = "X" then
      print "I got a new message: " + message.content   
   end if
end define

provider.register = onMessage
provider.subscribeToTopic("X")

Filtering

Topics are not the only means by which messages are grouped. Content-based filtering may be used to discriminate between relevant and irrelevant messages. Filtering may be consumer-side or provider-side.

Consumer-side Filtering

Advantages

The advantage of this approach is that the consumer may easily alter the type of messages it is interested in. For example, a news mobile application may receive all news but the user may initially select to view sports and weather news only. The user may decide to switch to finance news at a later time without the need to fetch new data from the provider—for example, when the user is on the underground train with no mobile reception.

Disadvantages

Consumer-side filtering is not truly a pattern, but the lack thereof. Thus, avoiding client-side filtering (or strict grouping by topic) is only feasible when the expected quantity and weight of the messages is reasonable in terms of scalability and bandwidth constraints.

Provider-side Filtering

Advantages

The consumer is relieved from implementing the filtering process. The required bandwidth and associated volumetrics only need to accommodate the messages that are relevant to the consumer.

Disadvantages

The provider has to be configured a priori with the required filters. It could be argued that a specific filter configuration would be, in effect, a topic. Alternatively, the consumer may be able to submit filter parameters interactively, but such feature may not be supported by the messaging framework in a native fashion.

Use of Specific One-Way Patterns

As we said before, the Publish/Subscribe MEP is typically implemented using the One-Way MEP. If the pure asynchronous One-Way MEP is chosen, certain limitations apply:

One-Way Pattern Activity Limitations
Asynchronous Subscription The provider may not receive the subscription event
Asynchronous New Message The consumer may miss out new messages

SOAP

In SOAP, the Publish/Subscribe MEP typically abides either to the WS-Eventing or the WS-Notification specifications.

Request/Call-back

The Request/Call-back Message Exchange Pattern (MEP) is that in which the consumer registers a call-back function with the provider. Such a function is normally used by the provider to notify the consumer when the request is completed; however, it may also carry additional data.

This pattern is a form of bidirectional One-Way MEP or Asynchronous Request/Reply. What makes it special is that the endpoint and/or function to be used by the provider is defined in an ad-hoc fashion at runtime rather than being defined in advance.

Pseudocode example:

define myFunction
   print "The food is ready"
end define

provider.cookFood(myFunction)