Socket.IOをRedisで分散する
ここ最近、Socket.IOとRedisをいじっていてはまったりしたものの、RedisでSocket.IOのメッセージを分散できたようなのでそれのメモ。
- 2つのnode.jsが立っていて、それぞれクライアントからメッセージを受け取る
- 2つのnode.jsは1つだけあるredisのmasterにメッセージを送る
- redisのmasterは2つあるslaveそれぞれにメッセージをPUBLISHをする
- 2つあるredisのslaveはSUBSCRIBEでメッセージを受け取る
- 2つのnode.jsはそれぞれが監視しているredisのslaveからメッセージを受け取る
図としては以下のような感じ。(わかりづらい)
<--- <----------------------------
client node.js redis(slave)
---> ---- --->
| |
| |
---> ----
redis(master)
---> ----
| |
| |
---> ---- --->
client node.js redis(slave)
<--- <----------------------------
package.jsonは以下のとおり。
{
"private": true,
"dependencies": {
"redis": "^0.12.1",
"socket.io": "^1.2.1",
"socket.io-emitter": "^0.2.0",
"socket.io-redis": "^0.1.4"
}
}
server.jsとして以下のようなものを書く。
var io = require('socket.io')(process.env.PORT || 3000),
redis = require('socket.io-redis');
io.adapter(redis({
pubClient:
require('redis').createClient(
process.env.PUB_PORT,
process.env.PUB_HOST,
{}),
subClient:
require('redis').createClient(
process.env.SUB_PORT,
process.env.SUB_HOST,
{ return_buffers: true })
}));
io.on('connection', function(socket) {
console.log('connection');
socket.on('message', function(data) {
console.log('message');
console.log(arguments);
socket.broadcast.emit('message', data);
});
socket.on('disconnect', function() {
console.log('disconnect');
console.log(arguments);
});
});
socket.io-redisにredisのインスタンスを渡す際に、return_buffers
がtrue
に指定されていないとnode.jsが落ちる(OS X 10.10.1)ことがあった。
指定しなくても落ちない環境もある(OS X 10.9.5)のだけど、理由がわからない。
実際に動作させる。
Redisを起動する。以下には3つコマンドを書いているが、それぞれ別のセッションから実行して起動する。
デーモン化してしまえば1つのセッションで済むのだが、今回はログを見たかったのでtmuxの画面分割をして3つとも表示しておいた。
$ redis-server --port 6379 --loglevel debug
$ redis-server --port 6380 --loglevel debug --slaveof 127.0.0.1 6379
$ redis-server --port 6381 --loglevel debug --slaveof 127.0.0.1 6379
node.jsでSocket.IOサーバを起動する。
これもRedisと同様にそれぞれ別のセッションから実行する。
$ DEBUG=* PORT=3000 PUB_HOST=127.0.0.1 PUB_PORT=6379 SUB_HOST=127.0.0.1 SUB_PORT=6380 node server.js
$ DEBUG=* PORT=3001 PUB_HOST=127.0.0.1 PUB_PORT=6379 SUB_HOST=127.0.0.1 SUB_PORT=6381 node server.js
クライアントにはiocatを使う。
これもRedisと同様にそれぞれ別のセッションから実行する。
$ iocat -v --socketio http://127.0.0.1:3000
$ iocat -v --socketio http://127.0.0.1:3001
これで、それぞれのiocatから適当に文字を入力してEnterを押すと一方に入力した文字が表示されると思う。
ついでに、socket.io-emitterからそれぞれのクライアントに対してメッセージを送ってみる。
$ node
> var s = require('socket.io-emitter')({ host: '127.0.0.1', port: 6379 });
undefined
> s.emit('message', 'Hello!');
Hello!
が送られていると思う。
今週はSocket.IOとRedisをずっと調べていて、少しはわかるようになってきた。
上記のものだとRedisのmasterが単一障害点となっているので、それをどうすれば解決できるのか気になる。いくらか考えて調べてみたけど機能的にできなかったりした。
この構成でどれくらいのパフォーマンスがになるのか調べていないので、それを先に試験をしてみたほうが良いかもしれない。