在亞洲做即時功能:WebSocket 伺服器放在哪裡很重要
· Hibira
為什麼距離決定了即時通訊延遲的下限、從香港到亞洲各地的下限是多少,以及如何量測你自己使用者的往返時間。

聊天訊息、報價跳動或「對方正在輸入」的提示,只要在大約十分之一秒內送到,感覺就是即時的。這段時間大部分花在網路上,而網路時間大多由你選擇的一件事決定:WebSocket 伺服器放在哪裡。
距離決定下限
光在光纖中每毫秒大約前進 200 公里,約是真空中光速的三分之二。所以到 1,000 公里外伺服器的一趟往返,至少要 10 毫秒,這還沒算路由、排隊和處理的時間。實際的海纜不會沿著大圓走,路由器也會增加延遲,所以實測的往返時間常常是這個下限的兩到四倍:我們從香港到台北實測約 30 ms,而下限是 8 ms。
以下是從 Hibira 目前所在的香港(AWS ap-east-1)到亞洲各城市的下限:
| 從香港到 | 大圓距離 | 最短往返時間 |
|---|---|---|
| 台北 | 810 公里 | 8 毫秒 |
| 馬尼拉 | 1,120 公里 | 11 毫秒 |
| 曼谷 | 1,730 公里 | 17 毫秒 |
| 首爾 | 2,090 公里 | 21 毫秒 |
| 新加坡 | 2,590 公里 | 26 毫秒 |
| 東京 | 2,880 公里 | 29 毫秒 |
| 雅加達 | 3,270 公里 | 33 毫秒 |
| 美國維吉尼亞州 Ashburn(美東) | 13,090 公里 | 131 毫秒 |
這些是物理限制,不是效能測試:沒有任何網路能比這更快,你的實際數字只會更慢。最後一列是用來對照的。很多美國服務把即時基礎架構放在美東,Pusher 的預設 cluster mt1 也在那裡。Pusher 確實有亞洲的 cluster,如果你在用 Pusher,請檢查你的應用程式建立在哪個 cluster。
為什麼即時功能比網頁更在意延遲
網頁在載入時只需要往返幾次。即時功能則是每次互動都要付出往返時間:
- 輸入中提示和游標每秒會送出好幾次用戶端事件,往返太慢就會跟不上使用者。
- 聊天要從一位使用者到你的伺服器、再到 WebSocket 服務、最後送給其他人,每一段都加上它的距離。
- 線上狀態(presence)在成員加入或離開時更新,更新太慢會讓已經離開的人看起來還在線上。
- 重新連線發生在手機切換網路時,需要 TLS 握手加上重新訂閱,等於連續好幾次往返。
每一段都要算
當你的後端發布一則事件,它會經過:
- 從使用者的操作到你的後端,
- 從你的後端到 WebSocket 服務的 HTTP API,
- 從 WebSocket 服務到每一個訂閱的用戶端。
第 1 段和第 3 段取決於使用者在哪裡,第 2 段取決於你的後端在哪裡。如果後端在東京、WebSocket 服務在維吉尼亞州,兩位台北使用者之間的一則事件就要橫越太平洋兩次。把 WebSocket 服務放在使用者附近,可以的話,也把後端放在它附近。
自己量量看
量測範圍: pusher:ping/pusher:pong 量到的是已建立 WebSocket 連線到即時服務端點的往返時間,不是事件從後端發布到訂閱者收到的完整延遲,也不包含首次連線時間。後端、廣播分發及瀏覽器渲染都不在這項量測內。建議在實際使用者網路(包含行動網路)多次測量,觀察中位數和 p95;DNS、TLS、路由與壅塞也會影響結果。
表格只是起點,真正重要的是你使用者的網路。Pusher 協定有一個 ping 訊息,伺服器收到會立刻回應,所以不用發布任何事件就能量往返時間。把下面存成 ping.mjs,用 Node.js 22 以上的版本執行(內建 WebSocket):
// node ping.mjs YOUR_APP_KEY [host]
const [key, host = 'ws.hibira.io'] = process.argv.slice(2);
const ws = new WebSocket(`wss://${host}/app/${key}?protocol=7&client=js&version=8.4.0`);
const times = [];
let sent = 0;
const ping = () => {
sent = performance.now();
ws.send(JSON.stringify({ event: 'pusher:ping', data: {} }));
};
ws.onmessage = (msg) => {
const { event } = JSON.parse(msg.data);
if (event === 'pusher:pong') times.push(performance.now() - sent);
if (event !== 'pusher:connection_established' && event !== 'pusher:pong') return;
if (times.length < 20) return void setTimeout(ping, 250);
times.sort((a, b) => a - b);
console.log(`best ${times[0].toFixed(1)} ms, median ${((times[9] + times[10]) / 2).toFixed(1)} ms`);
ws.close();
};從使用者所在的地方執行:在你服務的每個國家各開一台雲端主機,或請同事用家裡的網路跑一次。要比較的是中位數;最佳值則顯示路由離下限有多近。行動網路還會再增加延遲,所以也用手機熱點測一次。
想看真實使用者的數字,可以記錄 pusher-js 從 connecting 到 connected 花了多久,連同使用者的國家一起送到你的分析工具。
我們的規劃
Hibira 目前在香港,因為它大約位在東亞和東南亞的中間。下一個準備中的區域是東京,再來是新加坡。每個應用程式會固定在一個區域,你可以選離大多數使用者最近的那一個。
如果想在切換之前先看看你自己使用者的數字,免費方案就足以對你自己的應用程式執行上面的腳本。