【问题标题】:Simple PHP long polling chat script, too simple?简单的PHP长轮询聊天脚本,太简单了?
【发布时间】:2011-04-07 02:26:27
【问题描述】:

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

【问题讨论】:

  • 简单是我们程序员追求的目标

标签: php chat long-polling


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    尖叫支持 AJAX。

    今天在how to send JavaScript responses to PHP 上查看我的帖子。完全没有理由让您的脚本必须循环。


    编辑:我对 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 使用;无需循环。

    【讨论】:

    • 我很确定 OP is 使用 AJAX 和 long-polling,正如问题所述。 en.wikipedia.org/wiki/…
    • @Pablo:一些可能对您有所帮助的教程:css-tricks.com/chat2net.tutsplus.com/tutorials/javascript-ajax/…anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat。 AJAX 肯定是要走的路。
    • im 使用 AJAX,加上 im 在服务器循环以最小化 AJAX 请求。
    • @Lucanos,感谢您的参考。在我开始构建自己的项目之前,我在研究中遇到了所有这些项目,因为它们缺少一些我感兴趣的功能。
    • @Pablo:(刚刚自学了长循环。)您预期的访问者/使用配置文件中是否有任何特定特征使您选择循环解决方案?我知道它会减少 AJAX 请求,但这也意味着服务器可能会与许多在循环中等待的并发访问者捆绑在一起(我认为这可能会给访问者带来同样多的问题)。跨度>
    【解决方案3】:

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

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

    【讨论】:

    • 使用 sleep() 函数的长轮询在纸面上听起来不错,在本地测试机器上运行时甚至更好。但是在实时服务器(共享主机)上没有那么多,它给服务器带来了太大的压力。我最终决定只保留 ajax 请求,而不是长轮询。我还创建了一些逻辑来根据活动级别和情况增加和减少 ajax 请求率。
    • 感谢您的回复,我会看看我的长轮询进展如何,然后决定是离开还是拒绝
    【解决方案4】:

    您可以尝试使用根据 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;
    

    【讨论】:

    • 和我的回答一样,所以我同意。
    • 您如何确保文件不会因同时访问和可能同时被写入而损坏?
    • 对于同时访问,FS 锁定文件而不破坏它。如果发生这种情况,将不会进行任何更改。发生这种情况的机会可能有 50 分之 1。机会太低,不能让它成为一个表演者。另外,如果您使用数据库,也会发生同样的问题。从理论上讲,文件更好,因为它“释放”了锁并且比数据库更快地响应。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多