2010-09-02 106 views
8

我正在开发一个简单的聊天应用,每个房间可能有10到20个用户。简单的PHP长轮询聊天脚本,太简单了?

向数据库查询新消息的脚本看起来对于获取的所有请求来说太简单了。

下面的代码回路新邮件,脚本的其余部分是刚开的变量,查询的建设和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秒钟,然后发送另一个请求 以获取新消息。

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

这个过程的推移和...

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

这里是现场演示,你可以用萤火http://pixbush.com/chat/chat.php

+3

简单是我们作为程序员**争取的**。 – 2010-09-02 01:56:57

+4

链接不再工作。 – kachar 2012-08-06 13:24:32

回答

2

尖叫 AJAX的检查。

今天看到我的帖子how to send JavaScript responses to PHP。没有理由为什么你的脚本应该不得不循环。


编辑:我的关于AJAX的坏处。当我编写IRC聊天机器人PHP-Egg时,我遇到了这个问题* 100。我解决它的方式(返回PHP 4天,请注意)是PHP到pcntl_fork(),并且每次有消息时它都会返回。好处是它不会像睡眠()那样100%阻塞CPU,并且速度比10秒或任何其他任何限制都快。


我又修改我的回答(对不起!):

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

然后,你会怎么做是

如果(filemtime( 'chat.log')>时间() - 5) { 回声json_encode(的file_get_contents( 'chat.log')); }

优点:有限的SQL使用;无需循环。

+3

我很确定OP *是*使用AJAX和** long-polling **,如问题所述。 http://en.wikipedia.org/wiki/Comet_(programming)#Ajax_with_long_polling – deceze 2010-09-02 01:57:02

+0

@Pablo:有几个教程可以帮助你:http://css-tricks.com/chat2/,http:// net。 tutsplus.com/tutorials/javascript-ajax/how-to-create-a-simple-web-based-chat-application/,http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery - 聊天/。 AJAX肯定是要走的路。 – 2010-09-02 01:57:49

+1

即时通讯使用AJAX,Plus im在服务器中循环以最小化AJAX请求。 – Pablo 2010-09-02 01:58:44

0

您可以尝试使用根据conversationId而不是数据库标记的文件,并检查文件是否已被“触摸”。另外,使用usleep和set_time_limit(用于Windows服务器)以毫秒为单位设置间隔并增加睡眠时间。实际上,它会延缓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; 
+0

和我的答案一样,所以我同意。 – 2010-09-02 02:43:11

+0

以及您如何确保文件不会因同时访问而被破坏,并且可能同时被写入? – Pablo 2010-09-02 23:19:29

+0

对于同时访问,FS锁定文件不会破坏它。如果发生这种情况,则不会做任何更改。这可能会有50个机会中的一个出现。这个机会太低,无法成为一名表演者。另外,如果您使用数据库,也会发生同样的问题。理论上,文件比较好,因为它“释放”锁定并且响应速度比数据库更快。 – sheeks06 2010-09-03 01:17:00

3

从你的描述,这听起来像你的沉默5秒的差距这违背了长轮询的好处。当呼叫从服务器返回(长或短)时,浏览器立即启动另一个请求。作为备份,在每次服务器调用时,浏览器都会启动一个比服务器端超时稍长的超时,但在返回请求时将其取消。如果服务器请求失败并且浏览器超时完成,请启动一个新请求。

1

我一直在进行网络聊天,并遇到同样的解决方案,以保持实时更新。所以,我想知道你是否弄明白了:是否使用sleep()函数来保持服务器端循环的好方法,或者最好是使用更多的ajax查询。 sleep()函数真的是一个好主意,当几个usres轮询时它不会停止服务器。

我看到meebo使用长轮询(时间之间的查询还取决于窗口焦点,我猜),所以SO聊天应用程序。似乎只是使用Ajax查询。这让我感到惊讶。

+1

使用sleep()函数的长轮询在纸上听起来不错,在本地测试机上运行时效果更好。但是在实时服务器(共享主机)上并不那么重要,这给服务器带来了太多的压力。我最终决定只保留ajax请求,没有长时间的投票。我还创建了一些逻辑,根据活动和情况的级别来增加和减少ajax请求率。 – Pablo 2010-11-25 20:02:51

+0

感谢您的回复,我会看看我的长轮询结果如何,然后决定是否离开或拒绝 – dr3w 2010-11-30 09:39:32