例如,打開一個「訂單列表」畫面,我會希望一筆新訂單建立時,會立即出現在目前的列表畫面,但不會影響我目前正在進行的操作。但這種設計對網頁架構的系統來說,是挺苛求的一件事。
大多數的網頁設計方式,採用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實作方式陸續提供整理)
沒有留言:
張貼留言