チャット等で、Roomに誰が入室しているのかを知る際の方法です。
Roomに入室したクライアント(socket id)一覧の取得は、socket.io 1.0以降では以下で取得できます。
io.nsps[yourNamespace].adapter.rooms[roomName]
ただしnode.jsをマルチプロセスで実行している場合、自プロセス以外のクライアント一覧は取得できないです。
そのためRedisのKVSを使い、以下のようにしました。
1.Roomに入室:
RedisのKVSに、各プロセス毎に、各ルームに入室したクライアント(socket id)一覧をセット
redisClient = redis.createClient(redisPort, redisIp, {})
process_name = localIp + 'p' + port # プロセス名:プロセスのIPとポート番号を結合した文字列
#
# socketをroomにjoinし、且つRedisにsetする
#
join = (socket, room)->;
socket.join room # socketをroomにjoin
key = room + ':' + process_name
value = []
if io.of('/').adapter.rooms[room]?
for socketId of io.of('/').adapter.rooms[room]
value.push socketId
redisClient.set key, JSON.stringify(value) # JSONの文字列として値をセット
roomName = 'works#' + work_id
join socket, roomName
下記のような感じでRedisにセットされます。
redis 127.0.0.1:6379[1]> keys *
1) "users#558:10.0.2.15p1338"
2) "works#1016:10.0.2.15p1337"
3) "works#1016:10.0.2.15p1338"
4) "users#10:10.0.2.15p1337"
redis 127.0.0.1:6379[1]> get works#1016:10.0.2.15p1337
"[\"1ZFRYWVeyrMPaiqOAAAD\"]"
redis 127.0.0.1:6379[1]> get works#1016:10.0.2.15p1338
"[\"vuturxDpPBL2ZHuLAAAK\"]"
2.クライアント一覧を取得:
RedisのKVSからワイルドカードを使ったkeysコマンドで該当するキーをすべて取得後、mgetコマンドで値を取得
#
# roomIDからsocket.idのリストを取得し、コールバック関数実行
#
clients = (room, cb, opts) ->
key = room + ':*'
redisClient.keys key, (err, replies)->
redisClient.mget replies, (err, vals)->
socketIds = []
for val in vals
socketIds = socketIds.concat JSON.parse val
cb(socketIds, opts)
roomName = 'works#' + work_id
clients roomName, (socketIds)->
console.log 'Roomに入室しているクライアント一覧:' + socketIds.join(',')
3.Roomから退室:
RedisのKVSの値を変更または削除
#
# socketをroomからleaveし、且つredisにsetする
#
leave = (socket, room)->
socket.leave room
key = room + ':' + process_name
value = []
if io.of('/').adapter.rooms[room]?
for socketId of io.of('/').adapter.rooms[room]
value.push socketId
if value.length > 0
redisClient.set key, JSON.stringify(value)
else
redisClient.del key
roomName = 'works#' + work_id
leave socket, roomName
一応これで実現可能ですが、クライアント一覧取得するためだけにredisのKVSを使うというのは、無駄にリソースを使っている感じであまりいい方法ではないように思います。
また、以下のような感じでプロセス終了時にredisのデータを全て削除する必要があります。
for signal in ['SIGINT', 'SIGHUP', 'SIGTERM']
process.on signal, ->
key = '*:' + process_name
redisClient.keys key, (err, replies)->
async.each replies, (reply)->
redisClient.del reply
process.exit 1
return
もっといい方法があれば、誰が教えて頂けたらと思います。