简单的PHP长轮询聊天脚本,太简单了吗?

发布于 2024-09-18 00:34:52 字数 883 浏览 14 评论 0原文

我正在开发一个简单的聊天应用程序,每个房间可能有 10 到 20 个用户。

对于它将收到的所有请求来说,在数据库中查询新消息的脚本看起来太简单了。

下面是循环获取新消息的代码块,脚本的其余部分只是获取变量、查询的构造和 json 响应对象:

$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;
    }
}

上面的块将每秒向数据库查询新消息,持续 10 秒,如果10 秒后没有新消息,它将通知浏览器。浏览器等待5秒,然后发送另一个请求 获取新消息。

但是,如果脚本发现新消息,浏览器会在从服务器收到新消息的响应后立即请求更多新消息。

这个过程一直持续下去......

那么我怎样才能进一步优化这个过程呢? 这已经是最好的结果了吗? 在我的本地服务器上工作正常,但我担心只有少数用户可能会因为所有请求和循环而使实时服务器(共享主机)超载。

这是实时演示,您可以使用 firebug http://pixbush.com/chat/chat.php

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

百变从容 2024-09-25 00:34:52

从您的描述来看,您似乎有 5 秒的沉默间隙,这抵消了长轮询的好处。当调用从服务器返回(长或短)时,让浏览器立即启动另一个请求。作为备份,每次服务器调用时,让浏览器启动一个比服务器端超时稍长的超时,但在返回请求时取消它。如果服务器请求失败并且浏览器超时完成,请启动新请求。

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.

迷爱 2024-09-25 00:34:52

这对 AJAX 来说是尖叫

请参阅我今天关于如何发送 JavaScript 响应的帖子到 PHP。您的脚本根本没有理由必须循环。


编辑:我对 AJAX 的看法不好。当我编写 IRC 聊天机器人 PHP-Egg 时,我遇到了这个问题 * 100。解决这个问题(请注意,早在 PHP 4 天)就是 pcntl_fork () PHP 并让它在每次有消息时简单地返回。好处是,与 sleep() 不同,它不会 100% 阻塞 CPU,并且比 10 秒或您对其施加的任意限制快得多。


我再次修改我的答案(抱歉!):

使用某种异步过程将文本转储到文件中。

那么你要做的是

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

:限制 SQL 使用;无需循环。

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.

画骨成沙 2024-09-25 00:34:52

我一直在进行网络聊天,并遇到了保持实时更新的相同解决方案。所以,我想知道您是否已经弄清楚:使用 sleep() 函数在服务器端保持循环是一个好方法,还是最好使用更多的 ajax 查询。 sleep() 函数真的是一个好主意吗?当多个用户轮询时它不会停止服务器吗?

我看到 meebo 使用长轮询(查询之间的时间也取决于我猜的窗口焦点),而 SO 聊天应用程序。似乎只是使用 ajax 查询。这让我想知道。

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.

半世蒼涼 2024-09-25 00:34:52

您可以尝试使用根据conversationId标记的文件而不是数据库,然后检查文件是否已被“触及”。另外,使用 usleep 和 set_time_limit(对于 Windows 服务器)来设置以毫秒为单位的间隔并增加睡眠时间。实际上,Usleep 会延迟 CPU 使用率,但如果文件发生更改,它仍然会立即触发。

这是我的聊天脚本的一部分。 =)

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;

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文