知乎的 comet 實現機制是怎樣的?

long-poll 還是 streaming?具體實現細節是怎樣的?


稍微補充一下,Chrome下使用的是JSONP Long Polling(究竟起什麼名字別計較),其他瀏覽器沒去看。

所謂JSONP,就是生成一個script標籤,把回調函數的名稱作為HTTP請求的參數。伺服器解析回調函數名,返回類似callback(data)的代碼,使瀏覽器可以跨域執行非同步請求。

至於知乎想跨域執行的原因,估計是想讓專門的伺服器來處理事件更新,並減少cookie的傳輸量吧。

而Long Polling是說伺服器在接收到這個請求後,阻塞而不立刻返回。如果有新的事件,或者達到超時時間(知乎目前設為30秒),再響應這個請求。

客戶端接收到響應後,再發起一個新的請求。

這比輪詢節省了無謂的開銷,但要在伺服器端保持大量長連接(Tornado正好擅長這個)。

想效率更高的話可以使用streaming,也就是在使用HTTP長連接的同時,將Transfer-Encoding設為chunked。當伺服器有新的事件時,就發送響應,但不關閉連接。

客戶端接收到響應後,處理該響應,也不關閉連接。

伺服器想再次發送響應時,直接使用現有的連接即可。

這比Long Polling節省了多次建立和關閉連接的過程,也不用擔心重建連接時錯過新事件(如果你沒在伺服器端保存的話)。但因為是單向的,就需要客戶端每隔一段時間發送心跳包,以證明客戶端還在線。

此外,這種方式明顯不能用JSONP實現,因為script標籤在全部載入完前是不會執行裡面的代碼的,因此一次HTTP請求,只有一次執行機會。

還想高效就用WebSocket吧。它仍然以HTTP作為傳輸層,在建立連接時,客戶端發起WebSocket握手請求,其頭部內容沿用HTTP協議的定義;伺服器接到請求後,返回驗證信息,並用101狀態碼提示切換到WebSocket協議。這樣一個WebSocket連接就建立好了。後續發送數據時,就不需要帶HTTP請求頭了。

它是雙向的,因此伺服器和客戶端都能隨時發送數據,且發送完不需要斷開連接。任意一方主動斷開連接時,對方都會收到onclose事件(伺服器端的API就自己實現吧);當然你也能直接用這個連接來發送心跳包,協議里還為此預留了2個操作碼(不過暫時沒有瀏覽器端的JavaScript API)。

並且,WebSocket也是能跨域的,所以從功能和性能上來說,它都足夠優秀了。

缺點就是低端的瀏覽器不支持,例如IE 6~IE 9。當然,支持WebSocket的Web伺服器也比較少(Tornado表示無壓力)。

另外,WebSocket的協議版本很多,各種瀏覽器支持的版本不一樣:https://en.wikipedia.org/wiki/WebSocket#Browser_support。例如iOS設備支持的hixie-76就是已被擯棄的,所以實現時還得考慮向下兼容的問題。

類似的還能通過Flash 、Silverlight和Java applet等瀏覽器插件來建立socket連接。缺點是沒裝插件就用不了,例如iOS設備支持WebSocket,但不支持後三者。


確切地說,應該是 callback-poll(謝 @葛亮 指教)

把昨天寫的一個答案搬過來了(已修正)——

打開 Firebug,發現知乎頁面會不斷產生一個 Net 請求:

http://comet.zhihu.com/update?loc=http%3A%2F%2Fwww.zhihu.com%2Fchannel=。。。callback=。。。

而它得到的 Response 內容顯然就是「新動態」、「新通知」所需要作內容更新的 DOM 數據。

繼續挖掘,發現 body 內的第一個元素就是 &