6/25/2007

淺談 Comet PUSH Server 架構

最近有機會碰到 Comet 或是 PUSH Server 這類的技術,剛好一直覺得之前寫的 Comet For Ruby on Rails and Mongrel不夠有感覺,所以再寫一次。這裡先得講一聲,我的認知就是 Comet == Server PUSH ,有錯請告知。

HTTP 統治網路界

簡單來說,一般的 Socket Programming 都是保持一個雙向的連線,可以送可以收。但是 HTTP Design 為了以下理由,設計方向為單向的。
  1. 提高 Server 處理效能
  2. 方便作 cache,Proxy 容易融入
  3. 方便作 Server Farm
  4. 網路發展初期,實體硬體狀況不佳,很容易造成 connection lose,單向傳輸可以有效避免瞬斷
我跟你 request ,你回我 HTML ,交易完成,connection close,This is HTTP。

如果以 HTTP 當初設計的初衷「文件交換」來看,我們發現到他設計的非常的好,也相當成功的在當初糟糕的網路環境下成功的生存下來。但是就是因為 HTTP 設計太成功了,導致現在網路業者一股腦兒將各式各樣的 App 放在 HTTP 的架構上,想要用當初只是為了文件交換的協定,變成一個包山包海的協定,自然產生很多問題。

HTTP 最大的問題在於即時性

要解決 HTTP 的即時性的問題,有 Polling 跟 Server PUSH 這兩種方式。

Polling

我們想要做到即時傳訊,Server 端觸發 Client 事件等等功能,以現行的 HTTP 架構下,都得利用 Polling 的方式來做到。Polling 其實也就是現在的 RSS Reader 的作法,RSS Reader 會每過半個小時,或是一個小時去問對方 Server 有沒有新文章。用 Polling來作即時性的功能,最大的好處在於容易寫,因為他完全符合 HTTP 的架構,但是最大的壞處在於不夠即時。以 RSS Reader 來說,每過一個小時去問有沒有新文章,代表的意思就是有一整個小時的空窗期,你得每過一個小時才知道有沒有新文章。

以 RSS Reader 的屬性來說,採用 Polling 有很多好處
  1. 我其實不需要知道立刻知道有多少新文章,使用者對於 RSS 的即時性需求沒那麼高
  2. Blog 系統多變,與其設定一個各 Blog 系統共通的 Socket 協定,不如設定一個共通的文件交換格式來的簡單
但是如果使用聊天室,即時訊息傳遞,Polling 就不是一個好主意了。聊天最怕不夠即時,所以必須加快 Polling Interval,但是會帶來「提高 Server 負載」的後果。你一分鐘 Polling 一次,跟一小時一次,前者的負載是後者的 60 倍。等於你用負載換來即時性。這個在人一多的情況下會造成相當大的負擔。

因為 Polling 在某些地方不是那麼的好,所以才有 Server PUSH 機制出現。

Server PUSH

Server PUSH 是建構在建立一個不中斷的 Socket Connection 下面。一個作法是使用 iframe ,送出的header中要把content-type設為” multipart/x-mixed-replace”,來保持一個不間斷的 HTTP Connection。另外一個作法就是用 Flash 來跟 Server 建立一個 xmlsocket。保持一個不中斷的 Connection ,代表 Server 有新訊息就可以直接傳給 Client,不用等待 Client 過來 request。

這樣可以帶來的好處就是,Server 有狀態更新才會有網路傳輸,如果沒有狀態更新,就不會浪費資源。而 Polling 是不管有沒有狀態更新,都得花上固定的網路傳輸成本,當然就會比較浪費資源。

舉個例子,考試成績快出來了,你每個小時都從宿舍去公佈欄看成績出來了沒,假設成績沒那麼快出來,你自然會花上許多無意義的時間在宿舍到公佈欄之間的往返上面( Polling )。但是如果老師有你的手機(不斷線的 Connection ),成績公佈老師就打電話給你,告訴你的成績(Server Push)。這樣不是好多了嗎?

但是你會問,假設老師打電話給你,你在收不到訊號的地方怎麼辦?假設班上人數太多,老師不是就會打電話打到手軟?

這剛好講到 Server PUSH 的壞處,第一個壞處是假設 Client 網路不好,connection 造成瞬斷,那麼你的訊息就會消失不見了。另外一個壞處就是,持續性的 connection 很難做到 Load Sharing機制,通常大家都只會跟同一台 PUSH Server 作連接,這台 PUSH Server 在人一多就會產生大負載度的問題。這當然有辦法解決,只是這就已經要討論到很深的地方了。

雖然 Server PUSH 不是萬能的。不過整體來說,以傳遞「即時性需求高的訊息來說」,PUSH Server 帶來的好處絕對會比 Polling 來的好,這倒是無庸置疑的。

9 則留言:

Jeremy 提到...

相對比起來,flash player 在這方面做的特別好,不論是用老舊的 xml socket server 或性能好一點 rtmp protocol (data/audio/video) 都可以很輕鬆達成 socket connection & push/pull,更棒的是 server clustering 也很容易。

所以遇到需要 push 的場合,後端我可能會直接用 Flash Media Server 或 Red5(這個是 FMS的clone, 開源且支援 ruby)做,這樣輕鬆許多。

小影 提到...
作者已經移除這則留言。
小影 提到...

剛試用過 Juggernaut (一個 Rails 的 XMLSocket plugin ) 去作一個
chatroom (http://echo.reality.hk/)。

Flash XMLSocket 的確是很方便的工具,但技術上要開一個 socket port 普通的 Hosting 不易辦到、公司的 firewall
也會廢了它的武功、再加上它建基於 Adobe 的 Flash,一個封閉標準。

看過 ActiveMQ 的 AJAX 連接方法,雖然是 Push 但有 Polling 的效果 (定期Push、但如果沒有 event Push 的 event 會維持一段時間直至有 event 或 timeout),不知是否能跟 ActiveMessaging一起work,可以研究。(http://activemq.apache.org/ajax.html)

Jeremy Lu 提到...

rtmp 的一個特色是它也支援 rtmpt (rtmp over http) 可走 port 80,所 以 firewall 已不再是問題了 :D

小影 提到...

我作了個實驗用 Ruby on Rails + AJAX + Mongrel custom Http Handler + ActiveMQ 去作即時通訊的效果! 不用 Flash 了。http://www.reality.hk/articles/2007/07/01/736/

匿名 提到...

不喜欢server push

Sheva.wen's Arena 提到...

我最近在用GWT写这样一个Server Push的模型,我的应用是局域网的,网络状况不在考虑范围内,但服务器即时连接数很多,希望听听作者关于大负载状况下的问题以及解决心得。

MSN機器人 提到...

comet 會受到有些gateway / firewall 支援keep-alive程度的影響,BOSH是另一個好東西

MSN機器人 提到...

現在我們應該把焦點放在HTML5 的websocket