寫在前面:Ajax輪詢相信大家都信手拈來在用,可是有這么一個問題,如果一個網站中同時有好多個地方需要用到這種輪詢呢?就拿我們網站來說,有一個未讀消息數提醒、還有一個時實時加載最新說說、昨天又加了一個全網喊話,以后還會要有類似功能添加是肯定的,難道要為每個功能都創建一個獨立的輪詢?要知道輪詢請求中有大半是無用,會對服務器資源和寬帶造成巨大的浪費。因此在頁面中每增加一個輪詢點,對服務器的壓力及寬帶浪費都將成倍的增長。再考慮一個情況,如果當前網頁中需要的不僅是簡單的Ajax輪詢,而是Ajax長輪詢呢?比如在一個 WebIM 應用中同時與兩個好友聊天,是不是要為每個好友都建立一個長輪詢呢?這不科學,這也不現實,HTTP1.1 協議中規定,同一客戶端同一時間最多只能與服務器保持兩個連接,問題就變得很棘手了。於是想,所有的輪詢無非都是定時向服務器取消息,那么我們是不是只建立一個輪詢點定時向服務器取消息,而服務器則將客戶端所有需要的消息整理打包后一次性發送給該輪詢點,再由該輪詢點對返回的消息進行逐點分配處理,心動不如行動,程序員用代碼說話。
<!--客戶端代碼--> var rtmsg = { //請求地址 uri: U("public/RtMsg/rtmsg"), //要獲取的消息,可以根據需要動態增減,如從模板變量進行分配,這樣可以減少不必要的數據浪費寬帶 items:'UnreadCount,Allnet,newfeed', //建立連接 connect: function () { $.ajax({ url: rtmsg.uri, data:{'items':rtmsg.items}, type: 'post', dataType: 'json', success: function (res) { //請求成功后通過HadnleRes對返回的結果進行分配處理 rtmsg.HandleRes(res.data); } }) }, //將取回的結果分配給對應的處理程序 HandleRes: function (res) { for (x in res) { eval('rtmsg.Handle' + x + '(res[x])'); } }, //處理未讀消息數提醒 HandleUnreadCount: function (res) { /*…………*/ }, //處理全網喊話 HandleAllnet: function (allnet) { /*…………*/ }, //最新說說處理 HandleNewfeed: function (newfeed) { /*…………*/ } /*我還想要*/ /*我還可以再加*/ }; $(function () { //每隔10秒再來一次 setInterval(rtmsg.connect, 10000); //頁面加載完畢先來一次 rtmsg.connect(); })
<!--服務器端程序,注:我這里使用的是ThinkPHP框架--> <?php /** * 實時消息推送模塊 */ class RtMsgAction extends Action { /** * 模塊初始化 */ function _initialize() { //關閉session,連接占用session導至頁面等待 session_write_close(); //不限請求超時 set_time_limit(0); } public function rtmsg() { //接收要獲取的消息項 $items = $_REQUEST['items']; //收集消息 $rt = $this->collect(explode(',', $items)); //返回的消息格式大至如下 $rt = array( 'UnreadCount'=>array('status'=>1,'data'=>array('total'=>5,'system'=>2,'message'=>3)), 'Allnet'=>array('status'=>1,'data'=>array('消息一','消息二','消息三','data格式自定,方便客戶端處理為好'), 'Newfeed'=>array('status'=>0,'data'=>false) ) //返回json格式消息 echo json_encode($rt); } /** * 收集需要返回給客戶端的信息 */ public function collect($items) { //設置未傳items時默認獲取哪些消息 $items = $items?$items:array('UnreadCount', 'Allnet', 'Newfeed'); $rt = array(); //逐項獲取消息 foreach ($items as $v) { $rt[$v] = call_user_func(array(&$this, 'get' . $v)); } return $rt; } /** * 獲取用戶的通知統計數目 */ public function getUnreadCount() { /*獲取消息過程省略,返回信息格式如下*/ $data['status'] = '獲取信息狀態'; $data['data'] = '信息詳情'; return $data; } /** * 獲取全網喊話 */ public function getAllnet() { /*獲取消息過程省略,返回信息格式如下*/ $data['status'] = '獲取信息狀態'; $data['data'] = '信息詳情'; return $data; } /** * 獲取最新說說 */ public function getNewfeed() { /*獲取消息過程省略,返回信息格式如下*/ $data['status'] = '獲取信息狀態'; $data['data'] = '信息詳情'; return $data; } }
基實本模型的關鍵在於各消息的分類收集,及返回消息之后的分配處理,即代碼中以下兩個部分
//客戶端:將取回的結果分配給對應的處理程序 HandleRes: function (res) { for (x in res) { eval('rtmsg.Handle' + x + '(res[x])'); } }
//服務器端:逐項收集信息 foreach ($items as $v) { $rt[$v] = call_user_func(array(&$this, 'get' . $v)); }
至此Ajax輪詢復用模型就建好了,技術沒什么技術,關鍵是巧合,最后總結一下這種模型的優缺點。
優點:避免了客戶端多個輪詢點造成的服務器資源及寬帶浪費,可以動態更改需要獲取的消息項,便於管理。
缺點:沒有辦法為每個需要輪詢的點分配獨立的請求頻率,一般情況下無所謂啦,如果是在長輪詢中使用該缺點不復存在。
本文純屬原創,轉載請注明出處。