Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Socket.IO 1.0の変更点、内 部的な話 Node学園#13
Slide 2
Slide 2 text
自己紹介 github: nkzawa twitter: nkzawa socket.io, engine.ioなどにコントリビュートしたり、 socket.io関連モジュールをつくったり。
Slide 3
Slide 3 text
アジェンダ 1. Engine.IOとUpgrade 2. Middleware 3. バイナリサポート 4. Adapter 5. プロトコル
Slide 4
Slide 4 text
Engine.IO と Upgrade
Slide 5
Slide 5 text
engine.io ● トランスポートの違いを吸収して、websocketと同等の機能 を提供するライブラリ。 ● socket.io 1.0には名前空間やルーム、自動再接続などの 高レベルな機能のみ実装され、メッセージの送受信は engine.ioを通して行われる。 ● socket.io 0.9で採用されていたfallback方式ではなく upgrade方式でトランスポートを切り替える。
Slide 6
Slide 6 text
fallback 1. 最初にwebsocketで接続を試みる。 2. 接続できたら終了。 3. 接続できなかった場合、初期設定で最低10秒間タイムアウ トを待つ。 4. タイムアウトしたら、改めてpollingで接続を行う。 => websocketが使えない場合の初回接続速度に問題があっ た。
Slide 7
Slide 7 text
upgrade 1. httpのgetリクエストを行い、pollingの接続を確立させる。 2. upgradeできるか判定。設定次第でupgradeは行われず終 了。 3. pollingを維持したまま、並列にwebsocketで通信しパケット の交換ができるかどうかまで試す(probe)。 4. websocketがつながったらpollingが止まるのを待ち(メッ セージのロスを防ぐため)、メインのトランスポートを切り替 える。
Slide 8
Slide 8 text
サポートされるトランスポート ● websocket ● polling ○ xhr ○ jsonp ※polling時はxhrが優先的に使用される。
Slide 9
Slide 9 text
flashsocket ? 公式にはサポートされなくなった。flashsocketに限らず、別のト ランスポートが必要な場合は自作して追加する。 “removed flashsocket, moving to userland” https://github.com/Automattic/engine. io/commit/10261c52119f2912b72b55bec4df87d91b180525
Slide 10
Slide 10 text
upgrade関連オプション(client側) transports: [‘polling’, ‘websocket’] 使用するトランスポートを順番に設定する。[‘websocket’] と すると最初からwebsocketで接続することもできる。 rememberUpgrade: false 一度upgradeに成功した後に再接続すると、upgradeプロセ スを飛ばしてwebsocketで接続する。SSLの時などに有効 にするとよいらしい。
Slide 11
Slide 11 text
Middleware
Slide 12
Slide 12 text
middleware ハンドシェイクから接続までの間に、処理を挿入する仕組み。 expressのmiddlewareと似たようなもの。 io.use(function(socket, next) {});
Slide 13
Slide 13 text
0.9では 0.9ではauthorizationで同様のことができたが、あくまで認証用 の機能で拡張性は低かった。 io.set(‘authorization’, function(handshakeData, fn) { // 認証失敗 fn(null, false); });
Slide 14
Slide 14 text
1.0の認証 setメソッドとauthorizationは廃止(後方互換のため残されては いる)。代わりにmiddlewareを使って認証する。 io.use(function(socket, next) { // 接続を閉じる next(new Error(‘not authorized’);); });
Slide 15
Slide 15 text
ハンドシェイクデータ ハンドシェイク時のrequestオブジェクトにアクセスでき、そこか らcookieのパースやセッションの復元などが可能。 io.use(function(socket, next) { console.log(socket.request); next(); });
Slide 16
Slide 16 text
// expressのmiddlewareを使ってcookieをパースする var cookieParser = require(‘cookie-parser’)(‘my secret’); io.use(function(socket, next) { var req = socket.request; var res = {}; // ダミーのレスポンス cookieParser(req, res, next); }); io.on(‘connection’, function(socket) { console.log(socket.request.cookies); });
Slide 17
Slide 17 text
ネームスペース middlewareはネームスペース単位。 // デフォルトネームスペース。次の二つは同じ意味。 io.use(function(socket, next) {}); io.of(‘/’).use(function(socket, next) {}); // foo ネームスペース io.of(‘/foo’).use(function(socket, next) {});
Slide 18
Slide 18 text
実行順序 全てのクライアントは、デフォルトネームスペース(’/’)へ必 ず最初に接続され、’/’ のmiddlewareが実行される。 cookieパーサのように、常に先に実行したいmiddlewareは ‘/’ へ登録する。
Slide 19
Slide 19 text
// クライアント var socket = io(‘http://localhost:3000/foo’); // サーバ io.use(function(socket, next) { console.log(‘first’); // 先に実行される next(); }); io.of(‘/foo’).use(function(socket, next) { console.log(‘second’); // 後で実行される next(); });
Slide 20
Slide 20 text
バイナリサポート
Slide 21
Slide 21 text
バイナリサポート 1.0からバイナリデータを送受信できるようになった。オブジェク トや配列に埋め込むんだり、複数バイナリを同時送受信も可 能。 socket.emit(‘event’, new Buffer([0, 1, 2]));
Slide 22
Slide 22 text
examples // オブジェクトに埋め込み socket.emit(‘event’, {data: new ArrayBuffer(10)}); // 複数バイナリ socket.emit(‘event’, [binary1, binary2]); // acknowledge socket.on(‘event’, function(callback) { callback(new Buffer([1, 2, 3])); });
Slide 23
Slide 23 text
0.9でバイナリを扱う 0.9でもバイナリをbase64エンコードした文字列として送受信す ることはできた。ただし、エンコード/デコード処理が入り、データ サイズも増加するため効率はよくない。 socket.emit(‘event’, buffer.toString(‘base64’));
Slide 24
Slide 24 text
サポートされるデータ形式 ● Buffer (node) ● ArrayBuffer (node, browser) ● Blob, File (browser)
Slide 25
Slide 25 text
バイナリ送信できるトランスポート ● ◯ websocket ● ◯ XMLHttpRequest Level 2 ● ☓ 古い仕様のwebsocket ● ☓ XMLHttpRequest ● ☓ JSONP ※バイナリ送信をサポートしないトランスポートでも、base64エンコードされた文字列 として透過的にデータを送受信できる。
Slide 26
Slide 26 text
バイナリの扱いに注意 データサイズに制限はないが、メモリへ読み込むので、大きな ファイルの配信などには向かない。 この問題を解決するために、別モジュールによるstream送信の サポートが計画されている。
Slide 27
Slide 27 text
Adapter
Slide 28
Slide 28 text
Adapter 複数のサーバ間でメッセージをbroadcastする時に 使用される。0.9にあったStoreの代替。 io.adapter(Adapter);
Slide 29
Slide 29 text
socket.io-redis 0.9にあったRedisStore相当のadapter。socket.io本体には同 梱されていないので、別途インストールが必要。 var redis = require(‘socket.io-redis’); io.adapter(redis({host: ’localhost’, port: 6379}));
Slide 30
Slide 30 text
Storeとの違い ● Storeのget/set 相当のデータ共有機能がなくな りシンプルに。Adapterが扱うのはbroadcastの み。 ● 内部的に使っていたpublish/subscribeがなく なってスケールしやすい設計に。
Slide 31
Slide 31 text
socket.io 0.9のサーバ間共有設計 クライアントの接続データ(handshakeデータ, 所属ルーム等) は、Storeを通して他のサーバへ通知され保存される。 それぞれのサーバが、他サーバに接続しているものを含む全て のクライアントデータ(の一部)を処理し、保持するためスケール しにくい。
Slide 32
Slide 32 text
例: socket.io 0.9サーバ10台に、それぞれ1万クライアントが 同時接続する場合。 各サーバそれぞれで10万クライアントのデータが処理・保持さ れる。サーバを増やしても、一つのサーバが処理するデータ数 は変わらず、同時接続数に比例して増える。 サーバA: 10万 サーバB: 10万 … ※あくまでデータの一部なので全くスケールしないわけではないと思われる。
Slide 33
Slide 33 text
socket.io 1.0のサーバ間共有設計 各サーバは一切データ共有を行わない。 1.0のAdapterでは、broadcastしたパケットとそのオプション データだけが通知され、サーバには何も保存されないためス ケールしやすい。
Slide 34
Slide 34 text
例: socket.io 1.0サーバ10台に、それぞれ1万クライアントが 同時接続する場合。 各サーバは自身に接続している1万クライアントのデータのみを 処理・保持する。サーバを増やすことで、一つのサーバが処理 するデータ数を減らすことができる。 サーバA: 1万 サーバB: 1万 ...
Slide 35
Slide 35 text
プロトコル
Slide 36
Slide 36 text
プロトコルの変化 socket.ioの構成変更や機能追加に伴い、プロトコ ルの大幅な更新が行われた。 ● トランスポート層(engine.io)の分離 ● バイナリサポート
Slide 37
Slide 37 text
0.9のパケット コロン区切りのメッセージ [type] : [id] : [namespace] : [data] 例: 5::/nsp:{"name":"foo","args":["hi"]}
Slide 38
Slide 38 text
0.9のペイロード pollingで一度に複数のパケットを一括送信するの に使用される。\ufffd 区切りでデータ長 +パケットを 連結して繰り返す。 \ufffd [length] \ufffd [packet] ... 例: \ufffd32\ufffd5:::{"name":"foo","args":["hi"]}\ufffd ...
Slide 39
Slide 39 text
1.0のパケット 区切り文字とJSONのキー名がなくなり効率的に。 データはutf8エンコードされる。 [engine.io type] [type] [namespace] , [data] 例: 42/nsp,["foo","hi"]
Slide 40
Slide 40 text
1.0のバイナリを含むパケット バイナリ数が追加。バイナリデータはプレースホル ダで置き換えられ、別のパケットとして送られる。 [engine.io type] [type] [binary数] - [namespace] , [data] 例: 451-/nsp,["foo",{"_placeholder":true,"num":0}]
Slide 41
Slide 41 text
1.0のペイロード バイナリ送信をサポートするtransportでは、文字列も全てバイ ナリに変換される。パケットがutf8エンコードされるのはこのた め。 [0(string), 1(binary)] [length(一桁づつ)] 255 [packet] ... 例: [00 02 05 ff 34 32 2f 6e 73 70 2c 5b 22 65 63 68 6f 20 62 61 63 6b 22 2c 22 68 69 22 5d]
Slide 42
Slide 42 text
1.0のペイロード(文字列) バイナリ送信をサポートしないtransportでは、文字 列のまま送信。バイナリはbase64エンコードされ る。 [length] : [packet] ... 例: 19:42/nsp,["foo","hi"]
Slide 43
Slide 43 text
プロトコルの変更による影響 socket.io 0.9のプロトコルを実装していた別言語 のsocket.ioサーバ・クライアントは一切使用できな い。 => ユーザが0.9から1.0にアップデートする際の最 も大きな障害になると思われる。
Slide 44
Slide 44 text
まとめ ● engine.ioでより安定したsocket通信ができます。 ● middleware便利。モジュールを作って公開しよう。 ● バイナリも送受信できるようになった。 ● adapterでスケールしやすいアーキテクチャに。 ● 0.9から1.0への移行は難しくない。プロトコルの違いには注 意。
Slide 45
Slide 45 text
thanks!