Socket.IOがどれくらいリアルタイムなのかちょっと計ってみた

node.js+Socket.IOでリアルタイムアプリケーションが作れるわけだけど、WebSocket使うにしてもXHR-Polling使うにしてもサーバ経由して通信していれば当然タイムラグはあるわけで、それってどれくらい遅延するものなんだろう? と思っていろんなサーバで試してみた(追記しました)。

測定方法

var server = require('http').createServer(function (req, res) {
    require('data-section').get('html', function (err, data) {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(data);
    });
});
var io = require('socket.io').listen(server);
// io.set('transports', ['xhr-polling']);
io.sockets.on('connection', function (socket) {
    socket.on('ping', function (data) {
        socket.broadcast.json.emit('pong', {
            clients: Object.keys(io.sockets.clients()).length,
            data: data
        });
    });
});
server.listen(3000);

/*__DATA__
@@ html
<!DOCTYPE html>
<html>
  <head>
    <title>test</title>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script type="text/javascript">
var start = new Date().getTime();
var socket = io.connect();
var ua = window.navigator.userAgent;
socket.on('pong', function (data) {
  var delay = new Date().getTime() - data.data.datetime;
  if (data.data.ua === ua) {
    console.log('pong: ' + delay + ' ms on ' + data.clients + ' clients');
  }
});
socket.on('connect', function () {
  console.log('connect: ' + (new Date().getTime() - start));
  setInterval(function () {
    socket.emit('ping', {
      datetime: new Date().getTime(),
      ua: ua
    });
  }, 1000);
});
    </script>
  </head>
  <body>
  </body>
</html>
__DATA__*/

こんなかんじで、Socket.IOに繋いで1秒毎にnew Date().getTime()の値とuserAgentをサーバに送る。サーバはそれをそのまますべてのクライアントにbroadcastする。受け取った側はその瞬間の時間と受け取った値の時間の差分を算出する。これが、実際にどこかのクライアントからのメッセージが他のクライアントに届くまでのタイムラグになる、はず。

Chrome, Safari, Firefox, socket.io-clientから5接続ずつ繋いで合計20接続を維持する。同じアプリの別タブからのものだけがconsoleに出力されるので、Chrome(13.0.782.218 WebSocket使える)でそれをサンプリングしてみた。

ローカルサーバ(WebSocket)

node v0.4.11
まずはもっとも早いと思われる環境。ローカル環境でnodeサーバを立ち上げてlocalhostにアクセスする場合。

3 ms on 20 clients
2 ms on 20 clients
4 ms on 20 clients
4 ms on 20 clients
1 ms on 20 clients
3 ms on 20 clients
5 ms on 20 clients
2 ms on 20 clients
3 ms on 20 clients
3 ms on 20 clients
...

平均3.12ms。ほぼラグ無し。

ローカルサーバ(XHR-Polling)

  var io = require('socket.io').listen(server);
+ io.set('transports', ['xhr-polling']);
  io.sockets.on('connection', function (socket) {

とやって、無理矢理XHR-Pollingを使うように変えてみる。

40 ms on 20 clients
19 ms on 20 clients
19 ms on 20 clients
10 ms on 20 clients
17 ms on 20 clients
52 ms on 20 clients
29 ms on 20 clients
28 ms on 20 clients
22 ms on 20 clients
13 ms on 20 clients
...

平均23.17ms。やはりWebSocketの方が早いらしい。

さくらVPS(WebSocket)

CentOS 5.5, node v0.4.11
ローカルと同じものをさくらVPSで借りてるサーバ上で動かしてみた。

27 ms on 20 clients
10 ms on 20 clients
10 ms on 20 clients
8 ms on 20 clients
17 ms on 20 clients
9 ms on 20 clients
9 ms on 20 clients
11 ms on 20 clients
15 ms on 20 clients
9 ms on 20 clients
...

平均14.99ms。さすがにローカルよりは遅くなるけどかなり早い。

さくらVPS(XHR-Polling)

同様に強制的にXHR-Pollingにすると

12 ms on 20 clients
29 ms on 20 clients
13 ms on 20 clients
53 ms on 20 clients
21 ms on 20 clients
30 ms on 20 clients
13 ms on 20 clients
34 ms on 20 clients
20 ms on 20 clients
24 ms on 20 clients
...

平均26.77ms。こちらは意外とローカルと変わらないかも。

Joyent

JoyentのSmartMachines。WebSocketが普通に使える。
node v0.4.11

195 ms on 20 clients
204 ms on 20 clients
195 ms on 20 clients
204 ms on 20 clients
195 ms on 20 clients
204 ms on 20 clients
195 ms on 20 clients
204 ms on 20 clients
196 ms on 20 clients
205 ms on 20 clients
...

平均199.88ms。結構差が出るんですね…

DotCloud

DotCloudはWebSocket使えなくて、XHR-Pollingも通常は使えないので以前試した方法で無理矢理動かす。
node v0.4.10

341 ms on 20 clients
412 ms on 20 clients
279 ms on 20 clients
291 ms on 20 clients
234 ms on 20 clients
236 ms on 20 clients
310 ms on 20 clients
333 ms on 20 clients
295 ms on 20 clients
294 ms on 20 clients
...

平均330.28ms。

Heroku

HerokuのCeder StackでNodeを動かす。これもWebSocketは使えないので強制的にXHR-Pollingで動かす。
node v0.4.7

416 ms on 20 clients
339 ms on 20 clients
224 ms on 20 clients
326 ms on 20 clients
256 ms on 20 clients
260 ms on 20 clients
457 ms on 20 clients
371 ms on 20 clients
302 ms on 20 clients
208 ms on 20 clients

平均329.59。DotCloudと同じくらい?

まとめ

local WebSocket 3.12
local XHR-Polling 23.17
さくらVPS WebSocket 14.99
さくらVPS XHR-Polling 26.77
Joyent WebSocket 199.88
DotCloud XHR-Polling 330.28
Heroku XHR-Polling 329.59

いわゆるクラウドサービスは自分でサーバ用意しなくても気軽にアプリ作れて便利だけど、結構タイムラグは出るのでそのへんは注意した方が良いかも。それでも使うならやはりWebSocket使えるJoyentが良さげ。
WebSocket使えないブラウザでの速度も統一して計るべきだったかな…あとで計り直そうっと

追記

というツッコミをいただきました。確かにネットワークの遠さにとの関連も知りたいですね…
というわけでpingも調べつつ全部計り直してみました。

client (すべて Mac OS X 10.6.8):

ping Chrome Safari Firefox sio-client
local WebSocket 0.1155 1.82 3.44 124.35 3.45
local XHR-Polling 0.1155 68.54 19.28 201.39 11.00
さくらVPS WebSocket 3.8999 7.66 14.45 76.89 36.56
さくらVPS XHR-Polling 3.8999 83.28 36.91 291.91 263.05
Joyent WebSocket 221.0034 205.2 211.39 766.38 251.58
DotCloud XHR-Polling 249.8081 498.45 2302.03 622.19 1594.32
Heroku XHR-Polling - 512.06 518.21 471.19 895.93
Herokuにはpingが届きませんでした…。
なんか昨夜計ったものともだいぶ結果が違う…DotCloud x Safariの異常な数値は何だろう…? 計るタイミングやネットワークによって結構ぶれるのかなぁ
EC2の各リージョンとかでも試してみたいところですね。Flash Socketだとどうなるのか、なども気になるところです。