隨着人們對Web即時應用需求的不斷上升,Server Push(推送)技術在聊天、消息提醒尤爲是社交網絡等方面開始興起,成爲實時應用的數據流核心。這篇日誌試圖探討的即是各類適合於PHP的Push的實現方式以及其優劣。php
1. 什麼是Server Pushhtml
想象在聊天應用中,若是使用傳統的ajax來承擔消息的傳入,那麼通常是經過每隔必定時間拉取一次信息的方式實現,可是其實這種方式有大量查詢是浪費的。聊天等Web應用更須要服務器在特定時間來主動告知前端有新的消息(Push),而不是前端每時每刻問服務器:「來消息了嗎?」(Pull)。這也正是爲何這個技術常被叫作反向ajax。前端
其餘別名:Comet,反向Ajaxhtml5
2. 如何實現Pushnode
其實所謂的推送技術也沒有多麼複雜,目前從大類上有3種,一種仍然創建在ajax基礎上,還有一種創建在框架基礎上,最後一種拋棄了傳統的HTTP協議,使用Flash或者HTML5的WebSockets技術。接下來將對這三種類別產生的不一樣的方式進行探討。jquery
1) Ajax 長輪詢web
Ajax長輪詢從本質上來講仍然是一種pull,可是實時性較高,無用請求減小不少,是一種不錯的Push實現方案。不過它只減小了網絡上的無謂消耗。ajax
核心: 客戶端發起一個ajax請求,服務端將請求擱置(pending)或者說掛起,直到到了超時時間(timeout)或須要推送時返回;客戶端則等待ajax返回後處理數據,再發起下一個ajax請求。跨域
優勢: 兼容性較高,實現簡單瀏覽器
缺點: 對於php這種語言來講,若是要作到實時,那麼服務端就要承受大得多的壓力,由於擱置到何時每每是不肯定的,這就要php腳本每次擱置都進行一個while循環。
固然,若是服務器刷新每秒級,那尚可接受,只是實時性上退化了。
注意: 瀏覽器有鏈接數限制。我得出的結論是若是當前頁面上有一個ajax請求處於等待返回狀態,那麼其餘ajax請求都會被擱置(Chrome, Firefox已測)。若是頁面有通常ajax需求怎麼辦?解決方法是開個框架,框架中使在另外一個域名下進行Comet長輪詢,須要注意跨域問題。
PHP實現: Jquery+php實現comet
2) Frame 長鏈接
受到ajax啓發,出現了框架下的長鏈接。
核心: Frame中發起一個普通請求,服務器將其擱置;須要推送時輸出直接執行
腳本,而後繼續保持鏈接。若是擔憂超時問題能夠改爲框架論詢。
優勢: 與1同樣具備高兼容特性
缺點: 最大的問題是若是框架在載入,那麼瀏覽器就好一直顯示「載入中」,這就弱爆了(解決方法參見文末的相關閱讀資源)。一樣服務器也要能hold住大量循環……另外,是否有同域鏈接限制沒測試。
3) Flash/HTML5 WebSockets
用flash來發起WebSockets,秒殺前面一切問題。
優勢: 標準化, RealTime, Push
缺點: 服務器須要能應對WebSockets;還有若是既沒有Flash又不支持HTML5的怎麼辦?
PHP實現: Start Using HTML5 WebSockets Today
6) 使用兼容封裝層(socket.io)
以上每種方法都有優劣,那麼終極解決方案即是合在一塊兒!能WebSockets時候就WebSockets,不支持HTML5特性就退化到Flash,沒有Flash則退化到Ajax長輪詢。這也是個人Rainbowfish所採用的方式。
優勢: 高度封裝,編寫很是容易,幾乎不須要關心如何去實現的。實時,超低負載,高併發。
缺點: 其實算不上缺點,socket.io的服務器端要求是node.js,而不是php。
我的見解: 若是你是獨立主機,能運行程序,那麼socket.io配合node.js是個很是高效的選擇。爲何呢?由於它還能夠避免php的服務端高負載。
Rainbowfish的消息系統經過這種方式實現: 全部客戶端都經過socket.io掛在nodejs服務器上(注意: 只是掛着,不須要任何循環,由於它是事件驅動的);須要推送消息了,服務器就與nodejs通訊(好比訪問某個地址來實現),告訴它推送什麼消息到哪裏;nodejs收到推送信號後,則經過socket.io實時傳輸數據給瀏覽器。這個其實也是一條單向的路,由於nodejs服務器不具有與php通訊的能力,實際上也不須要,網頁上直接連php就能夠了。
3. 結束語
事實上,第一個方法(Ajax Long Pull)是一個不錯的方法,只是若是使用php完成的話服務器負載上有點大,但這實際上是通病;而最後列舉的socket.io方案徹底避免了這個問題,由於它屬於另外一種架構,而且這種組合也能夠配合幾乎全部的腳本語言實現push。
對於實時性要求很是高的應用,或許使用php實現實時部分並非一個好的選擇,將會面臨很是大的服務器負載(能夠經過編寫支持等待事件的擴展來解決這個問題);若是隻是消息提示等,則能夠調整服務器上刷新的間隔下降到秒的級別,負載尚可接受。不過不管哪一種用途,配合那些非阻塞語言或許纔是最好的選擇。
4. 相關閱讀
How to implement COMET with PHP
Start Using HTML5 WebSockets Today
==============================================================================================
comet研究[http://lync.in/research-on-comet/]
在Web應用中,客戶端的AJAX技術已經很是廣泛也很是深刻人心了,但與此同時,另外一些應用,諸如在線監控,實時數據顯示,即時通信等須要將後臺數據變化狀況實時顯示到前臺,這樣的由服務器push的行爲(也許會讓你想到blackberry)則須要另外一種方案來解決,也就是本文所要介紹的Comet —— 無需安裝插件,保持http長鏈接的服務器推方案。
如下兩點是方案中必須顧及到的。
Comet的客戶端與服務端交互流
業界對於Comet實現有兩種主要的解決方案:
這種方式就是由客戶端發出AJAX請求,而後服務端阻塞請求直至有響應或超時。客戶端在接收到服務端的指令以後會進行響應併發出新的請求。
從實現層面上來講,當XMLHttpRequest的狀態爲4也就是load的狀態時會進行客戶端處理,而Gecko(Firefox)和Webkit(Chrome,Safari)目前支持在readystate爲3的時候讀取(固然只能讀取到全部該請求已返回的串內容,因此須要自行肯定指令邊界),Trident(IE)目前若是中途去讀取會拋出錯誤,IE8中使用XDomainRequest能夠適當解決這個問題(參見Eric Law的COMET Streaming in Internet Explorer[])。
目前,開心網採用的是這一種方式。
這種方式是使用了iframe的機制,而後使得這個iframe請求一個特定的URL,並經過對這個頁面的加載不斷的從服務端抓回數據,這裏從服務端抓回的數據大可能是對頁面當前JavaScript函數的引用和操做。
這個方案的一個明顯不足之處是頁面會一直顯示正在加載,而這在IE上會更明顯。Google的天才們想到了用htmlfile的ActiveX控件來解決這個問題的方案,詳細描述能夠參見Alex Russell的What else is burried down in the depth's of Google's amazing JavaScript?
目前,人人網和GTalk採用的是這種方式。
除了文首所提到的通用性和性能以外,還有幾點是須要列入考量範圍的。
其實,Comet技術在AJAX大紅大紫的2005年以後的2006年時是業界一個很熱的討論點,目前的這兩種方式很是成熟,在dojo,dwr等前端框架中都已經有這樣的實現,而Bayeux協議的出現也已經在實質上訂下了一種業界的標準。
Comet的框架前端有Pushlet,dwr和dojo等,服務端有Jetty,Meteor,Orbited,Glassfish,Alpha,實現的產品語言也覆蓋了Java,C++,Python,Perl,Ruby,Erlang,.Net等。
下一代HTML5中的WebSocket會是Comet的一個新起點,但在那以前,在非插件的web層面應該不會有更進一步的討論與技術出現。
本文只是對Comet這個技術進行大致的概述,粗陋不明之處不免,在後續的文章中將會對WebSocket進行必定的解釋和演示。
參考資料:
Comet:基於 HTTP 長鏈接的「服務器推」技術
[http://www.ibm.com/developerworks/cn/web/wa-lo-comet/]
ps:上述文章應該夠你看明白的了。使用一種吧。但我如今尚未在項目用推技術,緣由,尚未來得及折騰,但在本地測試都很正常 。
如下提供protype 和 jquery的 +php實現的代碼例子。[例子代碼來自網上,已測試經過。好用]
http://bbs.php100.com/read-htm-tid-290215-ds-1.html