Simple PHP long polling chat script, too simple?
Asked Answered
A

4

8

Im working on a simple chat app, probably 10 to 20 users per room.

The Script that queries the database for new messages looks too simple for all the request it'll be getting.

Below is the block of code that loops for new messages, the rest of the script is just getting the variables, construction of the query and the json response object:

$sleepTime = 1; //Seconds
$data = "";
$timeout = 0;

//Query database for data
while(!$data and $timeout < 10){
    $data = getQuery($sql);
    if(!$data){
        //No new messages on the chat
        flush();
        //Wait for new Messages
        sleep($sleepTime);          
        $timeout += 1;
    }else{
        break;
    }
}

The block above will query the database for new messages every second for 10 seconds, if after the 10 seconds there are no new messages it will notify the browser. The browser waits 5 seconds and then sends another request to get new messages.

However If the script finds new messages, the browser will request more new messages instantly as soon as it gets the response with the new messages from the server.

This process goes on and on...

So how can i optimize this process any further? Is this as good as it gets? Is working fine on my local server, but im afraid that just a few users could overload a live server(shared host) with all the requests and the loopings.

Here is live DEMO you can check with firebug http://pixbush.com/chat/chat.php

Amalgamation answered 2/9, 2010 at 1:45 Comment(1)
Simplicity is what we as programmers strive for.Brandonbrandt
B
3

From your description, it sounds like you have a 5 second gap of silence which defeats the benefit of long-polling. Have the browser start another request immediately when a call returns (long or short) from the server. As a back up, with each server call, have the browser start a timeout slightly longer than the server-side timeout, but cancel it when a request is returned. If the server request ever fails and the browser timeout completes, start a new request.

Behoove answered 26/10, 2010 at 21:19 Comment(0)
C
2

This screams for AJAX.

See my post today on how to send JavaScript responses to PHP. There's no reason why your script should have to loop at all.


EDIT: My bad about the AJAX. When I wrote the IRC chatbot PHP-Egg, I ran into this problem * 100. The way I solved it (back in the PHP 4 days, mind you) was to pcntl_fork() PHP and have it simply return each time there was a message. The benefits are that it doesn't 100% block the CPU, unlike sleep() and is MUCH faster than 10 seconds or any arbitrary limit you put on it.


I'm revising my answer again (sorry!):

Use some sort of asynchronous process that dumps the text into a file.

Then what you'd do is

if (filemtime('chat.log') > time() - 5) { echo json_encode(file_get_contents('chat.log')); }

Benefits: Limited SQL usage; no need to loop.

Chit answered 2/9, 2010 at 1:53 Comment(7)
I'm pretty sure the OP is using AJAX together with long-polling, as the question states. en.wikipedia.org/wiki/…Motheaten
@Pablo: A few tutorials which may help you here: css-tricks.com/chat2, net.tutsplus.com/tutorials/javascript-ajax/…, anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat. AJAX is certainly the way to go.Beeman
im using AJAX, Plus im Looping in the server to minimize the AJAX requests.Amalgamation
@Lucanos, thanks for the references. I came across all of those projects in my research before i started building my own because they are missing some features i'm interested on.Amalgamation
@Pablo: (Just educated myself on long-looping.) Is there any specific characteristic in your expected visitor/usage profile which made you go with the looping solution? I get that it would reduce AJAX requests, but it also means that the server could be tied up with a number of concurrent visitors all waiting in loops (which may cause just as much of an issue for visitors, I would think).Beeman
@Lucanos: sleep() is actually a CPU-blocking operation (at least it was in PHP 4 days when I last attempted what @Amalgamation is trying). Meaning that if you have 100 users, that could easily lead to 100% CPU just on loops.Chit
@hopeseekr: Kinda figured that might be the case, hence the follow-up question asking Pablo the goals of using this long-loop functionality.Beeman
T
1

I've been making web-chat and came across just the same solution for keeping real-time updates. So, i wonder whether you have figured it out: is it a good way to keep looping server-side using sleep() function, or maybe it's better to use more ajax queries instead. And is sleep() function really a good idea and it won't halt server when several usres are polling?

I see meebo using long-polling (time between queries also depends on window focus i guess) while SO chat app. seems just to be using just ajax queries. So that makes me wonder.

Tutankhamen answered 23/11, 2010 at 20:1 Comment(2)
long polling using the sleep() function sounds good on paper and even better while running on local test machine. But on the live server(shared hosting) not so much, it puts too much stress on the server. I ultimately decided to keep only the ajax requests, no long polling. I also created some logic to increase and decrease the ajax requests rate depending on the level of activity and situation.Amalgamation
thanks for the reply, i'll see how my long-polling goes and then decide whether to leave it or refuseTutankhamen
T
0

You could try using files labeled according to conversationId instead of a DB and just check if the file has been 'touched'. Also, use usleep and set_time_limit(for windows server) to set your interval in milisecs and increase the sleep time. Usleep actually, delays CPU usage but is still fires instantly incase the file has been changed.

Here is a section of my chat script. =)

define('SUCCESS', '__SUCCESS__');
define('FAILED', '__FAILED__');

$tmpLib = $TMPFOLDER;
$msgPath = $MSGFILE;

$timeout = $POLLSPEEDSEC;

$acct = new Account($tmpLib, $_GET['key']);

if (false === $acct) {
    return false;
}

$msg = new Message($msgPath, $acct);

$lastMod = !empty($_GET['ts']) ? $_GET['ts']: 0;
$lastMod = substr($lastMod, 0, 10);
$lastMod = (int)$lastMod;

$result = array();

$start = gettimeofday();
$prevMsg = $acct->getTemp('cache');

do{
    usleep(10000);

    if ($acct->getFileTime() >= $lastMod) {
        $result['account'] = $acct->getAllOnline();
    }

    if($msg->getFileTime() >= $lastMod) {
        $result['message'] = $msg->fetch();
    }

    if (!empty($result)) {
        $theMsg = json_encode($result);
        if ($theMsg != $prevMsg) {
            $acct->setTemp('cache', $theMsg);
            echo $theMsg;
            flush();
            exit;
        }
        $result = array();
        $lastMod = time();
    }

    $end = gettimeofday();
} while($timeout > ($end['sec'] - $start['sec']));

echo FAILED;
Twoup answered 2/9, 2010 at 2:38 Comment(3)
Same as my answer, so I agree.Chit
and how exactly do you make sure file does't get corrupted by been access simultaneously and possibly been written to at the same time?Amalgamation
For simultaneous access, FS locks the files not corrupt it. If this happens, no change will be done. There is maybe 1 out of 50 chances that this will happen. The chance is too low to make it a show-stopper. Plus, same issue also happens if you use a DB. Files are better, in theory, because it "releases" the lock and respond faster than DB's.Twoup

© 2022 - 2024 — McMap. All rights reserved.