How does Long Polling or Comet Work with PHP? - php

I am making a notification system for my website. I want the logged in users to immediately noticed when a notification has made. As many people say, there're only a few ways of doing so.
One is writing some javascript code to ask the server "Are there any new notifications ?" at a given time interval. It's called "Polling" (I should be right).
Another is "Long Polling" or "Comet". As wikipedia says, long polling is similar to polling. Without asking everytime for new notifications, when new notifications are available, server sends them directly to the client.
So how can i use Long Polling with PHP ? (Don't need full source code, but a way of doing so)
What's its architecture/design really ?

The basic idea of long-polling is that you send a request which is then NOT responded or terminated by the server until some desired condition. I.e. server-side doesn't "finish" serving the request by sending the response. You can achieve this by keeping the execution in a loop on server-side.
Imagine that in each loop you do a database query or whatever is necessary for you to find out if the condition you need is now true. Only when it IS you break the loop and send the response to the client. When the client receives the response, it immediately re-sends the "long-polling" request so it wouldn't miss a next "notification".
A simplified example of the server-side PHP code for this could be:
// Set the loop to run 28 times, sleeping 2 seconds between each loop.
for($i = 1; $i < 29; $i++) {
// find out if the condition is satisfied.
// If YES, break the loop and send response
sleep(2);
}
// If nothing happened (the condition didn't satisfy) during the 28 loops,
// respond with a special response indicating no results. This helps avoiding
// problems of 'max_execution_time' reached. Still, the client should re-send the
// long-polling request even in this case.

You can use (or study) some existing implementations, like Ratchet. There are a few others.
Essentially, you need to avoid having apache or the web server handle the request. Just like you would with a node.js server, you can start PHP from the command line and use the server socket functions to create a server and use socket_select to handle communications.
It could technically work throught the web server by keeping a loop active. However, the memory overhead of keeping a php process active per HTTP connection is typically too high. Creating your own server allows you to share the memory between connections.

I used long polling for a chat application recently. After doing some research and playing it with a while here are some things I would recommend.
1) Don't long poll for more than about 20 seconds. Some browsers will timeout. I normally set my long poll to run about 20 seconds and send back an empty response at that point. Then you can use javascript to restart the long poll.
2) Every once in a while a browser will hang up. To help add a second level of error checking, I have a javascript timer run for 30 seconds and if no response has come in 30 seconds I abandon the ajax call and start it up again.
3) If you are using php make sure you use session_write_close()
4) If you are using ajax with Jquery you may need to use abort()

You can find your answer here. More detail here . And you should remember to use $.ajaxSetup({ cache:false }); when working with jquery.

Related

Getting real time feedback from a server process [in PHP]

Requirement:
I need to run a background process (per a user request) that takes about 30 to 60 seconds to complete. I'd like to give the user some status feedback. Note: Toly is right, 'Background' is not required.
What's working:
The process prints about 20 status messages during this time and I retrieve them with a proc_open and listening on a read pipe using fgets. I can save those messages into a session var and using timestamps (to help debug) I can see that the session array is getting written to with these messages as the process progresses.
The Trouble:
My plan was to poll the server with ajax calls (every sec) to retrieve these session vars for display in the DOM. The bottleneck seems to be that the server cannot service the ajax request while it's still running the background process. Everything dumps out at once when the background process completes. From what I can tell, the issue is not with output buffering because using (debugging) timestamps saved with each process message shows the server is writing to the session var sequentially, so that's how I know the proc_open and pipe reads are working as I expect. The issue appears to be the server not being able to give the AJAX request it's JSON object until it is done with the process; or, probably more accurately, done with the loop that is reading the pipe.
Obvious Misconception:
I thought sending a process to the background (using &) might give me a solution here. Apparently I do not know the difference between a background process and a forked process. What benefit is gained - if any - by running a process in the background when doing so appears to make no difference to me in this scenario?
Possible Solutions:
I do not expect the user initiated process that runs this
process/scenario to be that heavy, but if there's something I can
build into this solution that would help a heavy load then I would
like to do that now.
Is this a multi-threading (pthreads) or a
multi-process (fork) solution?
Or, should I save a process id,
let go polling it with a while( .. fgets ..) statement and then
come back to the process after the server has serviced the ajax
request?
I suppose I could run fake status messages and then
response accurately when the results come back after completion.
The time to process the request is not dependent upon the user, so
my fake timing could be pretty accurate. However, I would like to
know what the solution would be to provide real-time feedback.
After google-ing one day for a technique to get the same behavior you are describing here I come up with an easy solution for my project.
A bit of important theory:
- session_start () and a set like $_SESSION["x"] = "y" will always lock the session file.
Case scenario:
- A - process.php - running through an ajax call
- B - get_session.php - a second ajax call;
The main problem is/was, that even if you set a $_SESSION inside a process that is being run through an AJAX it will always have to wait the for the session file to get unlocked and it will result into a sync between the two processes (A. + B.) - both finishing at the same time!
So, the easiest way to fix this matter and get a good result is by using session_write_close() after each set. E.g.:
%_SESSION["A"] = "B";
$_SESSION["x"] = "y";
session_write_close();
PS: Best approach is to have a customed set of functions to handle the sessions.
Sorry for the mark-up. I just created an stack account.
Why would you think that you need a background process? Also, where did you get the idea that you needed one?
A normal php script, with sufficient time out set, with flush() function used every step of the way will give you the output you need for your AJAX.
What's even easier, since you use sessions - AJAX request to a separate handler, that will just check what's in session, and if there is smth new - will return you the new part.
$_SESSION['progress'] = array();
inside process.php
$_SESSION['progress'][] = 'Done 5%';
// complete some commands
$_SESSION['progress'][] = 'Done 10%';
inside ajax.php
if(count($_SESSION['progress']) > $_GET['laststep']) {
// echo the new messages
}
inside your normal page
$.ajax('ajax.php', 'GET', 'laststep=1', success: function(data){ show(data);})
Something like that should work.

Determinate update time

As I'm currently in the process of making a forum system which is loading new posts/edits without having to refresh the page. Now, for the older browers which don't have an implentation of EventSource/WebSocket, I'd like to offer another option:
Every X seconds I'm GET'ing a PHP site which is echoing the five latest news. Afterwards, I'm simply checking which of those news weren't seen by the client yet and applying the changes to the page.
Now, my problem is: How would you determinate the X interval in which the client is retrieving new updates? I'd like to base it up the user's connections so that it isn't killing off his connection completely.
What would be your attempt at accomplishing this?
I would use long polling technique through AJAX in your case:
1) The client sends the AJAX HTTP-request to the server.
2) If there is an available data, server sends HTTP-request to client, otherwise instead of sending an empty response immediately, server holds the request and waits for information to become available (or for a suitable timeout event - for example, in every 25 seconds), after which a complete response is finally sent to the client.
3) After recieving the HTTP-respose, client immediately sends other HTTP-request to server.
I would do the following (code not tested, but you should get the idea). Use jQuery for simpler code.
function refreshNews() {
$.ajax({
url: "ajax-url"
}).done(function(data){
/** add code here */
setTimeout(function(){ refreshNews(); }, 30000); // 30 secs should be enough to read some headlines
});
}
refreshNews();
This way the refreshNews() function is only called after the data is received and shown to the user.
Just an idea: make a HTTP request and see how much it will take long and use it as the base! I'd repeat it, let say each 10 minutes to show how much I'm thinking about my clients!
I think it will be more resource-friendly on the server-side comparing to the long polling, especially for scripts like forums where people won't left the page for less than 10 hours. :)

Can we delay response of a PHP page until another flag request of same visitor?

I'm trying to make a chat script, but I don't want the visitor's browser to ask for a new message every 0.5 seconds. Instead, I want the server to send the new message immediately.
I thought I could send a request to the server, and if there aren't any new messages, the server will wait until there is one. This waiting time doesn't need to be long, perhaps 2-3 seconds.
I tried PHP's sleep() function:
while(1)
{
$temp=$db->query("SELECT * FROM chat WHERE (id=36 OR id=12) AND clock>".$last_time);
if ($temp->rowCount()!=0) break;
sleep(1);
}
This "wait until an interrupt" process works when other visitors break the loop, but when the visitor who starts the loop tries to send a message, he waits forever.
Am I going about this the wrong way?
EDIT
*System is IIS 7 - PHP Version 5.3.6
I'm not quite sure why you tagged facebook-chat here.
Anyways, that method is inappropriate and will over-load your server with database queries. You should use long-polling or websockets. It might sound like a lot to make the user poll for updates, but when done appropriately its fine.

Whats an alternative way of updating a page with new records as they are inserted into a table automatically without using jquery/ajax

I've wrote a small chat system using jquery, php, and mysql; however, I'm looking for some kind of technology that will only update a if a new record is inserted into a row. I feel like using jquery ajax calls every second to retrieve new records is really overkill and strenuous on my server.
You are looking for a Comet solution: http://en.wikipedia.org/wiki/Comet_%28programming%29
The idea, as pdr noted, is to the javascript continuously open an async request with the server. The server holds it open, but does not send anything until it determines there is something to send. The request will timeout on the javascript side after 10-20 seconds, after which it should re-open the connection.
This uses a 'subscriber' based model, by which the server will send out the chat message or what have you, to all clients which are subscribed, all at once. This saves you many database requests, as the server is the one asking for the requests, not the individual clients.
What you want is long polling. Basically, you make an XHR, and the server and PHP holdes the request open until new data is ready to be sent back.
You need to configure Apache not to timeout in this circumstances, so do some substantial research. Basically, the PHP looks like so...
set_time_limit(0);
while (TRUE) {
$db->query('SELECT `message` FROM `messages` WHERE `new` = TRUE');
if ($db) {
echo json_encode($db->results());
exit;
}
sleep(1);
}
Then, you make an XHR for this page, and it will stay open until new data is ready. Then, on the complete callback, update your page's state and make a new XHR.
This is a lot more efficient than polling for updates continually using XHR.
Make sure you do a lot of research because I believe Apache is going to think things are wrong if a PHP script hasn't stopped after 30 seconds or so. :)
There are a couple routes I know of that you can take.
Long polling. This is where the browser opens a connection to the server and does nothing until the server responds. Once the server responds or times-out (sends an empty response to the browser), a new long-polling request is made.
When going this route, you should use a server that does not rely on using a new thread for each request.
Web sockets. Again, you'll want a server that can handle requests without spawning a new thread every request. With web sockets, a connection is kept open between the client and servier, and unlike Long polling, doesn't time out. However, this isn't well-supported yet.
I highly recommend checking out http://socket.io/
The point of Ajax is that it's asynchronous. Can you not just wait at the server until there's a worthwhile response to send?
With standard HTML/CSS/JS, that's pretty much the only way since the browser can make requests of the server, not vice versa. The AJAX call shouldn't have to be very big at all. A chat system, by definition, is going to require hitting the server a lot.

comet style application with loops

Do all comet style applications require a loop somewhere in the application on the serverside to detect updates/changes? If no, please could you explain how the logic behind a loopless comet style application would work?
This kind of application will always require a loop, you need to periodically check for new data etc. Of course you can make the "loop" non-blocking by using an even-loop based approach, but in the end there's still a loop somewhere.
Just think about it for a moment, how would you make it work without a loop? I sure can't imagine a way that doesn't utilize a loop somewhere.
Short answer is, no, not all require a loop on the serverside.
Instead you can use long-polling AJAX calls from the browser to request data,
at which the server simply responds with the data and the browser waits until the response is gotten before sending a new request.
The solution could be stream_set_blocking. Use any possible blocking resource to be suspended by OS and wait for appropriate interruption.
Client side:
Ajax call to endpoint script (timeout for ajax e.g. 30 seconds - after 30 seconds initiate another one - after 30 seconds you will get response from server - script execution time reached)
If you will get response during 30 seconds handle response (async) and open new connection (as in comet done - I saw it in cometD client)
Server setup:
Setup apache timeouts (between request and data sent to 30-31 second), this is so apache will allow you to wait so much
set apache to allow lot of child instances (concurrent users * 1.5), but you need to be sure that you have enough memory for this amount of apache instances (+ memory used by php children)
Script one:
execution_time = 28
set shutdown_function in order to send response (timeout, but formatted and understandable for ajax if You need it)
you need to open file, empty one
enable blocking mode using stream_set_blocking for file stream
try read from file and you will get suspended until other process will write to file or timeout be reached.
As soon as script gets content in file written from other process it will get back and will send response. (this will trigger another ajax call and another slept process)
Worst thing is that you need to think how to get multiple reader scripts reading from same bus (file) without disturbing each other.
Also there could be that timeout will be exactly at that time when message will be written into bus.
(hope that this solution is not as bad as my English)

Categories