Synchronous vs. Asynchronous
Synchronous communication: The calling party requests a service, and waits for the service to complete. Only when it receives the result of the service it continues with its work. A timeout may be defined, so that if the service does not finish within the defined period the call is assumed to have failed and the caller continues.
Asynchronous communication: The calling party initiates a service call, but does not wait for the result. The caller immediately continues with its work without caring for the result. If the caller is interested in the result there are mechanisms which we'll discuss in the next paragraphs.
Be aware that the distinction between synchronous and asynchronous is highly dependent on the viewpoint. Often asynchronous is used in the sense of “the user interface must stay responsive all the time”. This interpretation often leads to the wrong conclusion: “…and therefore every communication must be asynchronous”. A non-blocking GUI usually has nothing to do with the low-level communication contracts and can be achieved by different means, e.g. parallel processing of the interactive and communication tasks. The truth is that synchronous communication on a certain level of abstraction can be implemented with asynchronous interfaces on another level of abstraction and vice versa, if needed.
File based communication is often considered to be asynchronous. One party writes a file but does not care if the other party is active, fetches the file or is able to process it. However it is possible to implement another layer of functionality so that the second (reading) party gives feedback, e.g. by writing a short result file, so that the first (writing) party can wait and poll for the result of the file processing. This layer introduces a synchronous communication over file exchange.
Communication over a database often is implemented by one party writing execution orders into a special table and the other party reads this table periodically and processes new entries, marking them as “done” or “failed” after execution. So far this is an asynchronous communication pattern. As soon as the first party waits for the result of the execution, this second layer introduces a synchronous communication pattern again.
The following chapters explain synchronous and asynchronous communication patterns in more detail, using web services as an example. The scenarios can also be used for other connectivity types.
Synchronous services are easy to implement, since they keep the complexity of the communication low by providing immediate feedback. They avoid the need to keep the context of a call on the client and server side, including e.g. the caller’s address or a message id, beyond the lifetime of the request.
Nevertheless some circumstances may require implementing asynchronous calls:
Expected round-trip durations are beyond time limits of the connection infrastructure (e.g. some web proxies close TCP connections after 2 minutes idle time)
Connections with a lack of stability (e.g. a dial-in network connection is not available all the time).
The caller is not interested in the result of the call or cannot wait for the result for some reason, e.g. it must free its resources.
In some cases the delivery of the asynchronous request can be assured by some other mechanism, e.g. message queuing, storing the request in a file system or creation of a T4x job.
The simplest asynchronous message exchange pattern is called fire-and-forget and means that a message is sent but no feedback is required (at least on that level of abstraction!).
The only possible feedback can come from the communication layer in case of an error in processing or sending the request, but never from the processing of the server.
If feedback on the server-side processing is required using a fire-and-forget transmission, some higher level implementation must add the necessary logic and data to establish a kind of “session” to link the feedback to the request. There are two possible patterns to implement this: Either the client repeatedly asks for the result of the processing on the server (polling) or the server calls a service of the client to report the feedback after it has finished processing (callback).
Polling causes potentially high network loads and is therefore not recommended. Nevertheless it has the advantage that the service provider (server) does not need to know about its clients and that no client needs to provide a service by itself.
On the contrary for the callback pattern, the receiver of the request (server) must by some means know how to send the feedback message and must know how to address the correct client (this information can be passed in the request or be stored statically). To collect the feedback some active instance on the caller’s side must listen to receive the feedback message (which in turn can be a fire-and-forget message). So the caller must become a service provider (“server”) by itself. Usually the client continues with its work after the request was fired instead of waiting. So there can be some interaction between the client and the callback instance to notify the client or the user of the arrival of the feedback. This interaction happens entirely on the client and is usually not a communication issue (instead you can imagine sending notification emails, notifying the GUI, push a workflow task to the user’s inbox or similar actions).
As previously stated the implementation of the message transfer may use synchronous or asynchronous transfers on a lower level. In the fire-and-forget example, the request might be transferred via TCP, which implicitly acknowledges each message. Even if the acknowledgment is being implemented, higher levels might not be interested in it. For the callback and polling scenarios, each message might be acknowledged, but from a high level perspective, there are only fire-and-forget messages.
Asynchronous behavior can be implemented for a T4x server by writing the message (input parameters) to the file system (where the T4x scheduler will poll for it) or by creating a job at the T4x job server. If the caller wants to be informed about the execution’s result, the T4x server needs to store the caller’s response address and some context information to be able to report the result back. This has to be done in the service implementation. T4x as consumer can handle this by providing a callback service. Another possibility is the caller periodically polling for the execution result, e.g. by looking for a result file in the file system or by asking the T4x job server for the result of a job identified by the job id. This can easily be done by providing an additional service asking for the job result.