2010-09-02 142 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