Engine.IO からみる Socket.IO の今後
intro
この記事は 東京Node学園祭2012 アドベントカレンダー : ATND の 24 日目の記事です。
Socket.IO の 1.0 が、出る出るといって全然出ないので、
やきもきしている方も多いと思います。
しかし、その裏では Engin.IO という、割りと良い感じの
ファミリープロジェクトができていて、
ちょうど先日 RealtimeConf でもその話がありました。
これは Socket.IO にも繋がるはなしなので、
今日はその Engine.IO の話をします。
参考はこのへん、
Engine.IO と Socket.IO (と WebSocket.IO)
Socket.IO は、 1.0 を視野に入れたあたりで、
関連プロジェクトとして、 Engine.IO と WebSocket.IO の
二つのプロダクトを新たに立ち上げました。
(この辺は Web+DB Press でも触れました。)
この二つは、開発が進んで多くの機能を単一のプロダクトに内包していた Socket.IO を、
より小さなモジュールに別けて、保守性・再利用性を高めるといったことを目的としています。
最終的には以下のような依存関係になります。
Socket.IO | Engine.IO | WebSocket.IO | (ws)
具体的な役割は次に説明します。
WebSocket.IO
過去にあった WebSocket プロトコルの仕様変更と、それがもたらしたブラウザの実装差異を吸収することを目的とした、
WebSocket のラッパモジュールです。(実際はそのほとんどを ws というモジュールがやっています。)
これで立てた WebSocket サーバは、どんなバージョンの WebSocket を実装していても、
同じインタフェースでやり取りできます。
対応プロトコルなど、詳細は以下を参照ください。
Engine.IO
WebSocket を実装しないブラウザに対して、 Flash Socket や Long Pooling へのフォールバックを提供します。
WebSocket の通信には WebSocket.IO を使っています。
だったんですが、今はここが少し変わっています。その話は後ほど。
詳細は以下。
Socket.IO
Engine.IO をベースにし、その上に namespace や authentication, custom events などの高レベル API を提供します。
しかし、この構成は Socket.IO@1.0 からで、 0.9 ではまだ Socket.IO は Engine.IO に依存しておらず、
従来通り自前でトランスポートを実装しています。
最近、更新があまりないのは、 Socket.IO を 1.0 にするために、
Engine.IO と WebSocket.IO の安定が欠かせないからです。
WebSocket.IO は割りと落ち着いて、現在は Engine.IO の実装を進めています。
しかし、 Engine.IO にも Socket.IO@1.0 タグがついた issue がちらほら残っているので、
まだちょっとかかるかなとは思っています。
フォールバックとアップグレード
(何回か書いた気がしますが)
Socket.IO では、これまで接続の確立に WebSocket をまず試し、
ブラウザや中間サーバなどの影響でダメだった場合のみ、
XHR や Flash にフォールバックする方式をとっていました。
しかし、この方法ではフォールバックが重なってしまった時、
実際に通信可能な接続が確立するまで、結構な時間がかかっしまう場合がありました。
問題は、ブラウザの実装というよりは Intermediaries (Proxy, Personal Wirewall etc)が多いことがわかっています。
Personal Firewall に関する Socket.IO のチームの調査は、 wiki にまとまっています。
そこで Engine.IO では、まず最初に最も安定して接続を確立できる XHR LongPooling による
接続を確立し、その後によりよい通信方法(FlashSocket, WebSocket)を試して、可能だったらそちらに
アップグレードするという方法をとることになりました。
(だから、このコンテキストの Upgrade は HTTP1.1 の Upgrade とは意味が違うので注意)
Socket.IO が Engine.IO に依存することになれば、 Socket.IO の挙動もそうなることになります。
これでイニシャルの接続速度の向上が見込まれます。
MetaEvent
いくつかのイベントが API として公開されることになりました。
現時点では以下の二つが予定されています。
(といっても、発表聞いて自分が実装したんですが。)
socket.on('packet', callback); // すべての packet (ping, message, close etc) を受信 socket.on('packetCreate', callback); // すべての送信
これで、より細かな内部状態を取得できます。
ロギングや可視化、フックなどがしやすくなるでしょう。
Visualization
Guillermo 達はリアルタイム Web の開発場面において、圧倒的に足りてないのが、
「内部の状態の可視化(Visualization)」だと考えています。
- コネクションの数
- その中の WebSocket 接続の数
- 発生した送受信の数
- レイテシ
- どのトランスポートを使ってるか
- etc
そこで彼らは、それを解決するために engine.io-monitor というプロジェクトの立ち上げを
考えているようです。
今はレイティを見るサンプルが engine.io の example に入っています。
engine.io/examples/latency at master · LearnBoost/engine.io · GitHub
中で何が起こっているのかをきちんと把握することはとても大事ですね。
Engine.IO のレイヤで見られれば、 Socket.IO の開発でも使えるはずなので、
この辺は、期待したいところです。が、ほんとまだサンプルしか無いので、どうなるのかなぁ。。
debug
ちょっとずれますが、Socket.IO ファミリーでは TJ の debug というモジュールを採用しています。
https://github.com/visionmedia/debug
これは、Node.js の起動オプション、環境変数、npm start の話 - Block Rockin’ Codes でも紹介した
環境変数を使ったデバッグ出力の応用です。
Node 本体には NODE_DEBUG で指定すると表示されるデバッグ出力が仕込まれています。
$ NODE_DEBUG=http,net index.js
これは基本標準モジュールのものですが、この方法に則って自分のモジュールに
デバッグを仕込むモジュールが debug です。
var debug = require('debug')('fuga') , http = require('http') debug('start'); http.createServer(function(req, res){ debug(req.method + ' ' + req.url); res.end('hello\n'); }).listen(3000, function(){ debug('listening'); });
このように、 require 時に ('http') と、このモジュールの名前空間を指定し、
debug() に出力したいログを仕込むと
$ DEBUG=fuga node index.js
などとした時に、仕込まれたデバッグを出力できます。
fuga を * にすれば、全てのモジュールのデバッグが出ます。
Socket.IO ファミリーはこれを使って、接続の確立やメッセージ通信などの
情報を出せるようにしてるので、開発時に DEBUG=engine.io などとすれば、
色々見えるようになって便利です。
また、 debug には掛かった時間を出したり、browserify を使ってブラウザで使う(出力先は localstrage)
もできるようなので、クライアントのデバッグも捗りそうです。
まとめ
ということで、 Socket.IO の更新頻度は下がっているし、
1.0 が出る出るといっておきながらなかなか出ないで、やきもきしてる方も多いと思いますが、
裏では engine.io、 websocket.io が進んでいるので、そちらも合わせて見てみると、
今後の動向が分かりそうということでした。
ちなみに、 http://realtimeconf.com/ は他にも色々おもしろいセッションがあったし、
録画もあるようなので、興味のある方は見てみると面白いかと思います。