Asynchronous Message Exchange Patterns
Asynchronous MEP’s bring many concepts from asynchronous OS development to the Internet. One aspect that’s interesting is the perceived speed up clients feel from asynchronous operations. This where the terminology non-blocking comes from, the idea being the client says, “contact me when its finished” as it submits a request. It’s like dropping clothes off at the cleaners; the request is going to take a few days to complete. The service provider says ‘no problem’ and the client is off to focus on other things while the long running request is handled by the provider.
If you’ve developed applications for the Web and had to create something to process enough data that a user would get frustrated waiting, you’re already familiar with the pattern. To solve this problem with human clients we determine some way to notify them, email or text message etc. Once we’ve identified a contact method asynchronous communication is possible. When processing is complete the user will be notified.
Computers interacting over a network are no different. Why make the client wait if an operation is going to take a long time to complete? A Web service that needs to process a large amount of data or just can’t produce a result quickly for a given operation is a good candidate for asynchronous design. Processing large datasets isn’t the only impetus for asynchronous design. Imagine a blog or forum where approval is required for comments or posts. What if posting on a blog or forum required waiting until an admin had time to review it? There likely wouldn’t be as much interaction online!
Building a Non-Blocking Web Service in PHP
Non-blocking means returning immediately, which really amounts to the definition of immediate. Collecting relevant data and providing a short response may be all it takes to merit closing the connection with the client. Queueing allows systems to respond quickly, collecting requests which are processed by dedicated backend systems with ample computing power. The action of queueing itself isn’t that cumbersome so lightweight frontend systems can handle lot’s of queueing actions. Imagine the cleaners hanging your clothes on a conveyer behind the counter.
A software queueing system might involve inserting a record into a database, storing a file on disc, or using an actual cueing service. There’s tons of ways to implement queues, but we’re thinking about the client and getting them on their way ASAP. What if receiving the request payload in full from the a client is enough to let it disconnect? Persisting the request to a database or disk might be too important, so it might be worth keeping the client around until then at least, so the result can be incorporated in the response. What if the response just takes some time to collect, for example a service that itself aggregates a number of services calls? The processing might not be intensive, but there would be some time spent waiting for the I/O of all those calls. There really isn’t any need for backend processing power, but we don’t want to leave the client hanging around waiting.., what if we could disconnect the client and continue handling the request in the original process?
<?php // this script can run forever set_time_limit(0); // tell the client the request has finished processing header('Location: index.php'); // redirect (optional) header('Status: 200'); // status code header('Connection: close'); // disconnect // clear ob stack @ob_end_clean(); // continue processing once client disconnects ignore_user_abort(); ob_start(); /* ------------------------------------------*/ /* this is where regular request code goes.. */ /* end where regular request code runs.. */ /* ------------------------------------------*/ $iSize = ob_get_length(); header("Content-Length: $iSize"); // if the session needs to be closed, persist it // before closing the connection to avoid race // conditions in the case of a redirect above session_write_close(); // send the response payload to the client @ob_end_flush(); flush(); /* ------------------------------------------*/ /* code here runs after the client diconnect */
Discussing the approach
By telling the server to continue processing after the client has disconnected and then telling the client the connection is closed the process used to handle the request from the client continues to run and lightweight processing can be handled without the need for queueing.
Don’t rely on the client
Some suggestions on the Web say the client should send a disconnect header as part of the original request, but this comes with drawbacks. The client won’t collect even an initial response from the server and there’s no guarantee each and every client will bother to do this.
If you enjoyed this post please consider sharing it!