views:

497

answers:

4

Hello,

I have done some google search on this topic and couldn't find the answer to my question.

What I want to achieve is the following:

  1. the client make an asynchronous call to a function in the server
  2. the server runs that function in the background (because that function is time consuming), and the client is not hanging in the meantime
  3. the client constantly make a call to the server requesting the status of the background job

Can you please give me some advices on resolving my issue?

Thank you very much! ^-^

A: 
  1. Ajax call run method longRunningMethod() and get back an idendifier (e.g an id)
  2. Server runs the method, and sets key in e.g. sharedmem
  3. Client calls checkTask(id)
  4. server lookup the key in sharedmem and check for ready status [repeat 3 & 4 until 5 is finished]
  5. longRunningMethod is finished and sets state to finished in sharedmem.

All Ajax calls are per definition asynchronous.

Rufinus
+1  A: 

You are not specifying what language the asynchronous call is in, but I'm assuming PHP on both ends. I think the most elegant way would be this:

  • HTML page loads, defines a random key for the operation (e.g. using rand() or an already available session ID)

  • HTML page makes Ajax call to PHP script to start_process.php

  • start_process.php executes exec /path/to/scriptname.php to start the process; see the User Contributed Notes on exec() on suggestions how to start a process in the background. Which one is the right for you, depends mainly on your OS.

  • long_process.php frequently writes its status into a status file, named after the random key that your Ajax page generated

  • HTML page makes frequent calls to show_status.php that reads out the status file, and returns the progress.

Pekka
If you want a unique identifier for a process created by the session, then a safer and more obvious candidate than rand() would be the session id. Also, the code referenced in the Contributed Notes will fail on non-Microsoft systems in unpredictable ways.
symcbean
Cheers symcbean, good points. I edited my answer.
Pekka
A: 

You could (although not a strictly necessary step) use AJAX to instantiate the call, and the script could then create a reference to the status of the background job in shared memory (or even a temporary entry in an SQL table, or even a temp file), in the form of a unique job id.

The script could then kick off your background process and immediately return the job ID to the client.

The client could then call the server repeatedly (via another AJAX interface, for example) to query the status of the job, e.g. "in progress", "complete".

If the background process to be executed is itself written in PHP (e.g. a command line PHP script) then you could pass the job id to it and it could provide meaningful progress updates back to the client (by writing to the same shared memory area, or database table).

If the process to executed it's not itself written in PHP then I suggest wrapping it in a command line PHP script so that it can monitor when the process being executed has finished running (and check the output to see if was successful) and update the status entry for that task appropriately.

Note: Using shared memory for this is best practice, but may not be available if you are using shared hosting, for example. Don't forget you want to have a means to clean up old status entries, so I would store "started_on"/"completed_on" timestamps values for each one, and have it delete entries for stale data (e.g. that have a completed_on timestamp of more than X minutes - and, ideally, that also checks for jobs that started some time ago but were never marked as completed and raises an alert about them).

Iain Collins
A: 

Have a google for long running php processes (be warned that there's a lot of bad advice out there on the topic - including the note referred to by Pekka - this will work on Microsoft but will fail in unpredicatable ways on anything else).

You could develop a service which responds to requests over a socket (your client would use fsockopen to connect) - some simple ways of acheiving this would be to use Aleksey Zapparov's Socket server (http://www.phpclasses.org/browse/package/5758.html) which handles requests coming in via a socket however since this runs as a single thread it may not be very appropriate for something which requiers a lot of processing. ALternatively, if you are using a non-Microsoft system then yo could hang your script off [x]inetd however, you'll need to do some clever stuff to prevent it terminating when the client disconnects.

To keep the thing running after your client disconnects then the PHP code must be running from the standalone PHP executable (not via the webserver) Spawn a process in a new process group (see posix_setsid() and pcntl_fork()). To enable the client to come back and check on progress, the easiest way to achieve this is to configure the server to write out its status to somewhere the client can read.

C.

symcbean