2011年3月28日

快快樂樂使用Java實作WebSocket (一)

雖然瀏覽器提供了「重新整理」的按鈕,點一下就可以更新網頁資料,但是對於愈來愈講求人性化設計的網頁設計,可不能要求使用者自己不斷去按重新整理。重新整理後的網頁,雖然更新了資料,但是對於瀏覽器或伺服器來講都很沒效率、也可能造成某些狀態的遺失。對於採用AJAX技術的網站,畫面的更新可以很動態且友善,而目前的趨勢則愈來愈講求資料更新的「即時( Realtime )」。

例如,打開一個「訂單列表」畫面,我會希望一筆新訂單建立時,會立即出現在目前的列表畫面,但不會影響我目前正在進行的操作。但這種設計對網頁架構的系統來說,是挺苛求的一件事。

大多數的網頁設計方式,採用Client Pull方式,是由瀏覽器端觸發新的HTTP連線,伺服器端才會回應新的資訊,因此必須不斷建立連線,例如每五秒重新查詢一次。若不考慮伺服器及頻寬負擔的情況,只要每秒都重新查詢,那樣資料的更新就會很即時,伺服器有什麼新資料都會立即呈現給使用者。但是對於線上有數百、數千甚至數萬人使用的網頁系統,肯定會造成相當大的負荷。

加長每次查詢的間隔時間,可以減輕伺服器的負擔,但是對使用者來說,就一點都不即時。而頻繁的查詢,即使擁有充足的頻寬及伺服器資源,仍是一件缺乏經濟效益的事,因為其中不少次的查詢,可能都是得到空的結果,因為根本沒有新的資料。

相反地,Server Push的模式,讓伺服器可以「主動」將新資料送出給瀏覽器。瀏覽器是一種Client端程式,並不會像Server程式可以建立一個TCP或UDP port等待連線。所以讓伺服器可以持續將新資料送至瀏覽器的作法,就是使瀏覽器打開一個網頁後,在背景仍保持著連線,伺服器可以透過這條連線,將新資料持續送出給瀏覽器。

只是,直到HTML5才有把「WebSocket API」列入標準,WebSockets協定讓瀏覽器可以和伺服器進行雙向溝通。儘管Google Chrome等現代瀏覽器已經實作WebSockets,但苦命的網頁開發者並無法享受新標準帶來的便利,因為要考慮到廣大的使用者的各種瀏覽器版本,強迫不支援新標準的瀏覽器必須升級,就等於將潛在的顧客阻擋在門外。

要做到「即時更新」的功能,還要顧及舊版瀏覽器的使用者,我們只能選擇多數當前使用者的瀏覽器都能通用的技術,還好許多Ajax Library已經費盡一番功夫(例如jQuery等),讓我們可以用比較輕鬆的方式,寫出跨瀏覽器的JavaScript程式,能夠在大多數人正在用的瀏覽器上運作。於是Comet模型的其中一種實作方式「Ajax with long polling」,成為目前一項實際有用的解決方案,是目前Facebook、Plurk等高流量社群網站實作動態更新的方式。

藉由Ajax的作法,JavaScript可以在背景透過XMLHttpRequest與伺服器建立連線,這其實還是一種「polling(輪詢)」的作法,但關鍵技巧在於每次polling查詢,若伺服器沒有新資料可以回傳,就會等待一段比較長的時間,直到伺服器上有新資料可以回傳,或是等待時間逾時為止。只要在背景維持一條long polling的查詢,就可以讓伺服器的新資料出現時,在很短的時間內就傳回給瀏覽器,而且不會很頻繁地建立、關閉連線,因為瀏覽器和伺服器之間每次建立HTTP連線進行request/response,都是相當耗費成本。而long polling盡可能地達到接近「即時」的更新效果,但成本卻相對低得許多。

這種概念不難理解,但實作起來仍會遇到技術上的問題。最大的問題在於long polling的查詢會維持比較長的時間,對瀏覽器來說這沒什麼問題,只是多一條連線佔用。但是對伺服器來說,假設一個人都是建立一條long polling的查詢連線,有多少人在線上,總共就會有多少條連線。相信設定過Apache等網頁伺服器的技術人員都會清楚,伺服器設定通常會有「連線數量上限」這一項目,預設數量通常不多,文件也不建議把它改得很高,因為伺服器要處理這些連線,必須建立新的process/thread,都是相當耗記憶體等系統資源。因此同一時間的連線數量如果過高,超過伺服器的負荷,可能會造成癱瘓、無法回應的後果。

待續... (本文將對Java平台的WebSocket實作方式陸續提供整理)

沒有留言:

張貼留言

lyhcode by lyhcode
歡迎轉載,請務必註明出處!