← Blog

Thời gian thực ở châu Á: vì sao vị trí máy chủ WebSocket lại quan trọng

· Hibira

Vì sao khoảng cách quyết định mức sàn của độ trễ realtime, mức sàn từ Hồng Kông tới khắp châu Á, và cách đo thời gian khứ hồi của chính người dùng bạn.

Một tin nhắn chat, một lần nhảy giá hay chỉ báo "ai đó đang gõ" sẽ có cảm giác tức thì nếu đến trong khoảng một phần mười giây. Phần lớn ngân sách thời gian đó dành cho mạng, và phần lớn thời gian mạng được quyết định bởi một điều bạn chọn: máy chủ WebSocket đặt ở đâu.

Khoảng cách quyết định mức sàn

Ánh sáng trong cáp quang đi được khoảng 200 km mỗi mili giây, tức khoảng hai phần ba tốc độ trong chân không. Vì vậy một vòng khứ hồi tới máy chủ cách 1.000 km mất ít nhất 10 ms, chưa tính định tuyến, hàng đợi hay xử lý. Cáp thật không đi theo đường tròn lớn và router thật thêm độ trễ, nên thời gian khứ hồi đo được thường gấp hai đến bốn lần mức sàn này: chúng tôi đo từ Hồng Kông tới Đài Bắc khoảng 30 ms, trong khi mức sàn là 8 ms.

Đây là mức sàn từ Hồng Kông, nơi Hibira đang chạy (AWS ap-east-1), tới các thành phố trong khu vực:

Đây là vật lý, không phải benchmark: không mạng nào vượt được, và mạng của bạn sẽ chậm hơn. Dòng cuối để so sánh quy mô. Nhiều dịch vụ Mỹ đặt hạ tầng thời gian thực ở miền Đông Hoa Kỳ; cụm mặc định của Pusher, mt1, cũng ở đó. Pusher có cụm ở châu Á, nên nếu bạn đang dùng Pusher, hãy kiểm tra ứng dụng của bạn được tạo ở cụm nào.

Đài Bắc8 msManila11 msBangkok17 msSeoul21 msSingapore26 msTokyo29 msJakarta33 msAshburn (miền Đông Mỹ)131 ms
Thời gian khứ hồi tối thiểu từ Hồng Kông, do tốc độ ánh sáng trong cáp quang quyết định

Vì sao điều này quan trọng với thời gian thực hơn với trang web

Một trang web trả giá khứ hồi vài lần khi tải. Một tính năng thời gian thực trả giá đó ở mọi tương tác:

  • Chỉ báo đang gõ và con trỏ gửi sự kiện client nhiều lần mỗi giây; khứ hồi chậm khiến chúng tụt lại sau người dùng.
  • Chat đi từ một người dùng tới máy chủ của bạn, tới dịch vụ WebSocket, rồi tới mọi người khác. Mỗi chặng cộng thêm khoảng cách của nó.
  • Presence (ai đang trực tuyến) cập nhật khi thành viên vào và rời; cập nhật chậm khiến người đã rời vẫn hiện là trực tuyến.
  • Kết nối lại sau khi điện thoại đổi mạng tốn một lần bắt tay TLS cộng các lần đăng ký kênh, nhiều vòng khứ hồi liên tiếp.

Tính mọi chặng

Khi backend của bạn publish một sự kiện, nó đi:

  1. từ hành động của người dùng tới backend,
  2. từ backend tới HTTP API của dịch vụ WebSocket,
  3. từ dịch vụ WebSocket tới mọi client đã đăng ký.

Chặng 1 và 3 phụ thuộc vào vị trí người dùng. Chặng 2 phụ thuộc vào vị trí backend. Một backend ở Tokyo publish qua dịch vụ WebSocket ở Virginia sẽ vượt Thái Bình Dương hai lần cho một sự kiện giữa hai người dùng ở Đài Bắc. Hãy đặt dịch vụ WebSocket gần người dùng, và nếu được, đặt backend gần nó.

Người dùnggửi tin nhắn
①
Backend của bạn
② HTTP API
Dịch vụ WebSocket
③ WebSocket
Mọi người đăng ký
Mỗi sự kiện đi qua ba chặng; chặng ① và ③ phụ thuộc vào vị trí người dùng

Tự đo

Phạm vi đo: pusher:ping/pusher:pong chỉ đo thời gian khứ hồi trên kết nối WebSocket đã thiết lập đến điểm cuối realtime, không phải độ trễ từ lúc backend phát sự kiện đến lúc người đăng ký nhận được. Phép đo không bao gồm thiết lập kết nối, fan-out hay dựng giao diện. Hãy đo nhiều lần trên mạng của người dùng thực (kể cả di động), so sánh trung vị và p95; DNS, TLS, tuyến mạng và tắc nghẽn đều có thể làm kết quả thay đổi.

Bảng chỉ là điểm khởi đầu; mạng của người dùng bạn mới là điều quan trọng. Giao thức Pusher có thông điệp ping mà máy chủ trả lời ngay, nên bạn đo được khứ hồi mà không cần publish gì. Lưu đoạn này thành ping.mjs và chạy với Node.js 22 trở lên (đã có sẵn 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();
};

Hãy chạy từ nơi người dùng của bạn ở: một máy chủ đám mây ở mỗi quốc gia bạn phục vụ, hoặc laptop của đồng nghiệp dùng mạng gia đình. Trung vị là con số để so sánh; giá trị tốt nhất cho thấy tuyến đường tiến gần mức sàn đến đâu. Mạng di động cộng thêm độ trễ riêng, nên hãy thử cả qua điểm phát sóng của điện thoại.

Để có góc nhìn từ người dùng thật, hãy đo thời gian pusher-js đi từ connecting tới connected và gửi về hệ thống phân tích cùng quốc gia của người dùng.

Chúng tôi đang làm gì

Hibira hiện chạy ở Hồng Kông vì nơi này nằm gần trung tâm Đông Á và Đông Nam Á. Tokyo là khu vực tiếp theo chúng tôi đang chuẩn bị, rồi đến Singapore. Mỗi ứng dụng sẽ được gắn với một khu vực, nên bạn sẽ chọn khu vực gần phần lớn người dùng nhất.

Nếu muốn xem số liệu của chính người dùng bạn trước khi chuyển, gói miễn phí là đủ để chạy đoạn script trên với ứng dụng của bạn.