在亚洲做实时功能:WebSocket 服务器放在哪里很重要
· Hibira
为什么距离决定了实时通信延迟的下限、从香港到亚洲各地的下限是多少,以及如何测量你自己用户的往返时间。

聊天消息、报价跳动或“对方正在输入”的提示,只要在大约十分之一秒内送达,感觉就是实时的。这段时间大部分花在网络上,而网络时间大多由你选择的一件事决定:WebSocket 服务器放在哪里。
距离决定下限
光在光纤中每毫秒大约前进 200 公里,约为真空中光速的三分之二。所以到 1,000 公里外服务器的一次往返,至少要 10 毫秒,这还没算路由、排队和处理的时间。实际的海缆不会沿着大圆走,路由器也会增加延迟,所以实测的往返时间常常是这个下限的 2 到 4 倍:我们从香港到台北实测约 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 毫秒 |
| 美国弗吉尼亚州阿什本(美东) | 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 目前在香港,因为它大约位于东亚和东南亚的中间。下一个准备中的区域是东京,然后是新加坡。每个应用会固定在一个区域,你可以选离大多数用户最近的那一个。
如果想在切换之前先看看你自己用户的数字,免费套餐就足以对你自己的应用运行上面的脚本。