最近HerokuでSocket.io依存のサービスを運用する機会があったので手順だけメモ。
(日本語記事あまり見つからないので、もしかしたら socket.io-redis
がこの問題をも解決してくれているのかな...クラスタリングするんでしょ?って忖度してくれるような)
症状
Heroku上でクラスタリングしたsocket.io(node.js)との接続が確立できない。
エラーメッセージは下記2種類。ポーリングで接続成功した後に、websocketにupgradeしようとして失敗しているような感じ。最初に接続確立したときと違うサーバにつなぎに行こうとして怒られてしまっているみたい。
Failed to load resource: the server responded with a status of 400 (Bad Request)
WebSocket connection to ......... failed: WebSocket is closed before the connection is established.
手順
- heroku側で
http-session-affinity
をONにする -
socket.io
のfallback optionをwebsocket
優先にする
現時点ではChromeやFirefoxであれば1の対処だけでOK。
Mac/iOS Safariやnode.jsのsocket.io-clientなどの場合は2の対処までしてやる必要があります。
HerokuのHttp Session AffinityをONにする
詳しくは コチラ
heroku features:enable http-session-affinity
この問題を調べるとsiticky session
を使え!って出てくるのだけど、 heroku
はsticky session
使えないよ!というスタックオーバーフローのページが沢山出てくる。公式的にはsession-affinity
って機能で似たような事を提供しているそうですが、それよりも前の記事も結構見かけるので、やはり記事の書かれた時期って大事ですね。
client側のsocket.ioのfallback optionをwebsocket優先にする
デフォルトではpollling
、websocket
の順番で接続をしていくsocket.ioですが、クラスタリングされたサーバに接続する場合は順番を指定して、websocket
から繋ぐようにする。
const options = {
transports: ['websocket', 'polling' ]
};
const socket = io.connect( "URL" ,options);
付録
socket系 x クラスタリングで発生する問題としては、セッションが各nodeで共有されないことくらいで、broadcastのときに困るくらいだろうとタカをくくっていました。Broadcastの問題に関しては別のソリューションで解決していたので、今回はたまたまsocket.io-redis
を使っていませんでした。もしかしたらこの問題自体、socket.io-redis
が一緒に解決しちゃってくれる可能性もあります。
ちなみにsocket.io、以前はwebsocketから順番にfallbackしていっていたのを、どこかのバージョンで逆にするようにしています。そのほうがwebsocket非対応のオールドブラウザでの接続確立までの時間が短くなるという理由らしいです。