A service has identified a conversation partner.
How can a consumer request information from a provider?
- Message channels often transport messages only in a single direction. For a bi-directional exchange of information, two channels may be necessary.
- Some connection-oriented channels, such as TCP/IP allow sending and receiving data over one connection. Still, the exchange involves two separate messages and message formats.
Use two messages, a request message and a response message.
The Asynchronous Request-Response conversation involves the following participants:
- The Requestor initiates the conversation by sending a Request message amd waits for a Response message.
- The Provider waits for incoming Request messages and replies with Response messages.
As with most conversations, when using Asynchronous Request-Response over asynchronous message channels, the requestor is responsible for matching the Response message to the appropriate Request. Because the asynchronous nature of the conversation, the Requestor can engage in more than one Asynchronous Request-Response conversation at one time, which results in response messages potentially arriving in a different order than the requests were sent. This can happen because some requests may be processed more quickly (or by a different service instance) than others. The Requestor should therefore equip messages with a unique Correlation Identifierto tie the messages to the conversation.
Conversation States
The Asynchronous Request-Response conversation has a single state, "Waiting for response". The service provider does not have to keep a conversation state because all it does is process a Request and subsequently send a Response message. This makes the Asynchronous Request-Response conversation stateless from the Provider's perspective and also implies that subsequent conversations can be serviced by different Providerinstances.
The Requestor can avoid tracking the conversation state if the response message contains all data necessary for the initiator to process it, "flattening" the conversation into a Pipes and Filters-style message flow.
Some Providers allow the Requestor to pass through additional data of their choice, which the Provider copies from the Request message to the Response message. This can help the Requestor avoid keeping local state, but it also increases network traffic and can cause security concerns as the Requestor could be manipulated by injecting data into Response messages.
Error Handling
The Requestor must be prepared to handle the following error conditions:
- A Response message may never arrive, or not arrive within an expected time interval. This can happen because channels are not reliable, very slow, or because the service provider failed. Because the Requestor cannot tell why the message is not received it leaves the Requestor in a state of uncertainty about the Provider's state. To address the error condition, the consumer can either resend the message, resulting in Request-Response with Retry, or Ignore Error.
- The Response message can indicate an application-level error condition. This may happen for example if the request contains invalid data and the service provider indicates that it did not process the request.
- The Response message may be ill formed or otherwise invalid, preventing the initiator from processing it successfully.
- Because Request and Response messages travel over separate channels, the Requestor may receive a Response message for a different request than expected. In some cases it may even receive a (possibly well-formed) Response to a request that it did not even make. This can happen for example, if the channel is implemented as a persistent message queue and a faulty client crashed before it consumed the Response message, causing it to remain on the channel. When the client restarts and sends a new request, it may be reading the previous response from the channel. If the Response message is not self-contained, the Requestor does not have much choice but to discard this message.
Comparing with Fire-an-Forget
The similarity between the implementation of Request-Response and the use of two Fire-and-Forget interactions highlights the fact that the patterns convey more than simply the technical solution -- they convey the intent behind the solution. If the fact that the response message returns to the original initiator is purely coincidental, one would express the interaction as two Fire-and-Forget interactions, highlighting the fact that the sameness of initiator and recipient of the second message is coincidental and is not core to the intent of the solution.
Comparing with Remote Procedure Call
This basic conversation is often compared to (or confused with) Remote Procedure Invocation, also known as Remote Procedure Call (RPC). However, the two patterns' intent is quite different: RPC's purpose is to invoke a method on another component and to return the results to the invoker. Asynchronous Request-Response does not tie specific semantics to the exchange of messages. The message exchange may be used to invoke a method, query information, or the reply may simply acknowledge the receipt of a message. RPC is also often tied to a synchronous client-side programming model, which makes the conversation look like a method call on the client side. This provides convenience for developers, who are used to working with synchronous method calls, but can result in brittleness and poor performance as the simple programming model masks the inherent challenges of the networked interaction, such as latency, partial failure etc.
Related patterns: Correlation Identifier, Remote Procedure Invocation, Ignore Error, Pipes and Filters, Request-Response with Retry