8

我正在开发一个简单的聊天应用程序,每个房间可能有 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查看

4

4 回答 4

3

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

于 2010-10-26T21:19:35.067 回答
2

这为 AJAX尖叫

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


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


我再次修改我的答案(对不起!):

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

那么你要做的是

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

好处:有限的 SQL 使用;无需循环。

于 2010-09-02T01:53:34.837 回答
1

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

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

于 2010-11-23T20:01:39.110 回答
0

您可以尝试使用根据 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;
于 2010-09-02T02:38:43.320 回答