【Node.js】socket.io + cluster で 400 (Bad Request) + Connection closed before receiving a handshake response

socket.io + cluster を使っていたら、クライアント側(HTML + JS)で以下のエラーが発生した。

WebSocket connection to 'ws://192.168.33.10:3000/socket.io/?EIO=3&transport=websocket&sid=nP02s_VbJ4I968puAAAO' failed: Connection closed before receiving a handshake response
socket.io.js:2919 GET http://192.168.33.10:3000/socket.io/?EIO=3&transport=polling&t=1427000334124-80&sid=nP02s_VbJ4I968puAAAO 400 (Bad Request)
socket.io.js:2919 POST http://192.168.33.10:3000/socket.io/?EIO=3&transport=polling&t=1427000334135-81&sid=nP02s_VbJ4I968puAAAO 400 (Bad Request)
socket.io.js:2919 GET http://192.168.33.10:3000/socket.io/?EIO=3&transport=polling&t=1427000334940-84&sid=-fAvPE6I6SZYIfvfAAAP 400 (Bad Request)
socket.io.js:2919 GET http://192.168.33.10:3000/socket.io/?EIO=3&transport=polling&t=1427000336165-87&sid=f6yPfADpOMnHbokQAAAP 400 (Bad Request)

なんかポーリングしてる???

結論から言うと、クライアント側のJSを以下のように修正すると動作した。
------------------
var chatRoom = io('http://192.168.33.10:3000/chat');

var chatRoom = io('http://192.168.33.10:3000/chat', { transports: [ 'websocket' ] });

                                  • -

transport に websocket を指定しただけ。

socket.io は websocket, htmlfile, xhr-polling, jsonp-polling など複数の方法で通信を確立させる。
つまり、websocketをサポートしていなければ xhr-polling で通信できたりする。

以下の記事によると明示的に transport を指定しないと最適な接続方法を見つけるために複数の接続方法を試しているっぽい。
http://stackoverflow.com/questions/26374425/websocket-handshake-in-node-js-socket-io-and-clusters-not-working

clusterを利用するとクライアントの接続先ノードが複数になっていて、ラウンドロビンで割り振られている。
なので、pollingするたびに接続先ノードが変わる(socketIDが変わる)のでエラーになっている。
これはWebサーバを複数扱う際にロードバランサーでバランシングすることによって
ユーザーのセッションが維持できない問題と同じイメージ。

上記コードのtransportは接続方法をwebsocketのみに指定したので、
pollingを試すことがなくなり、
エラーが出なくなっている。

今回はwebsocketのみを利用するので
この対応でOKだけど
websocketが利用できないクライアント環境をサポートする場合には使えない。

その場合は接続先ノードを固定する sticky-session というモジュールがある。
https://github.com/indutny/sticky-session

ちなみに、「sticky session」という単語は
ユーザーのセッションを維持するためにリクエストを特定サーバに固定する方法のこと。
これはnodeとかsocket.ioとかは関係なく、
元々はwebサーバのセッション管理方法であったみたい。