socket.io는 일단 기본 store가 메모리 기반이여서 프로세스를 하나 띄우면 거기서 밖에 socket.io 접속자들을 공유를 못해요. 그래서 socket.io에서는 store를 redis로 변경해서 여러 프로세스에서도 공유를 할 수 있는 옵션을 제공을 해요.

var RedisStore = require('socket.io/lib/stores/redis')
  , redis  = require('socket.io/node_modules/redis')
  , pub    = redis.createClient()
  , sub    = redis.createClient()
  , client = redis.createClient();

io.set('store', new RedisStore({
  redisPub : pub
, redisSub : sub
, redisClient : client
}));

이렇게 하면 된답니다.

그래서 한 번 해보면, redis를 설치해야해요. 저는 windows환경이라 virtualbox에 ubuntu설치하고 redis를 설치했어요. 아 그리고 나중에 redis도 1대로 못버티면 redis도 clustering해야하는데, 이건 나중에 삽질 해보고....

이게 원리가 redis에서 제공하는 pub/sub기능을 이용해서 하는건데, 하나의 서버에서 pub/sub를 만들고 연결해놓고 나중에 현재 서버에서 접속이 들어오면 다른 서버들에 publish를 해서 알려주고, 다른 서버는 구독중이기에 연결된 데이터를 받을 수 있습니다.

대략 소스는 이러합니다.

server.js

var redisInfo = {
    host: '192.168.56.1',
    port: 6379
};
var app = require('http').createServer(handler),
    io = require('socket.io').listen(app),
    fs = require('fs'),
    RedisStore = require('socket.io/lib/stores/redis'),
    redis = require('socket.io/node_modules/redis'),
    pub = redis.createClient(redisInfo.port, redisInfo.host),
    sub = redis.createClient(redisInfo.port, redisInfo.host),
    client = redis.createClient(redisInfo.port, redisInfo.host);

if (process.argv.length < 3){
    console.log('ex) node app <port>');
    process.exit(1);
}
app.listen(process.argv[2]);

function handler(req, res) {
    fs.readFile(__dirname + '/index.html',
        function (err, data) {
            if (err) {
                res.writeHead(500);
                return res.end('Error loading index.html');
            }
            res.writeHead(200);
            data = data.toString('utf-8').replace('<%=host%>', req.headers.host);
            res.end(data);
        });
}

io.configure(function(){
    io.set('store', new RedisStore({
        redisPub: pub,
        redisSub : sub,
        redisClient : client
    }));
});

io.sockets.on('connection', function (socket) {
    socket.on('message', function(data){
        socket.broadcast.emit('message', data);
    });
});


index.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>socketio redis store</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io.connect('http://<%=host%>');
       
        $(document).ready(function(){
            socket.on('message', function(data){
                $('#chat').append('<li>' + data.message + '</li>');
            });
           
            $('#btnSend').click(function(){
                send();
            });
            $('#inputText').keyup(function(e){
                if (e.keyCode == 13){
                    send();
                }
            });
        });
        function send(){
            var message = $('#inputText').val();
            if (message.length < 1){
                return;
            }
            socket.emit('message', {message:message});
            $('#chat').append('<li>' + message + '</li>');
            $('#inputText').val('');
        }
    </script>
</head>
<body>
    socketio redis store...<br />
    <input type="text" id="inputText" />
    <button id="btnSend">보내기</button>
    <ul id="chat">
    </ul>
</body>
</html>

node app.js 10001

node app.js 10002

두 개 띄워놓고 localhost:10001, localhost:10002 접속하면 두 서버에서 같은 방에 있는 것처럼 채팅을 할 수 있습니다.

redis는 기본적으로 127.0.0.1로 bind를 해서 외부에서 접속을 할 수 없습니다. 그래서 /etc/redis/redis.conf 파일을 수정해서 bind 127.0.0.1을 주석처리하거나 알맞게 수정하시면 됩니다.

socket.io는 참 잘해놓은게 다른 것은 전혀 신경쓸 것이 없이 store옵션만 바꾸어서 socket들의 정보 저장위치를 변경하게 만들어 놨습니다. 나중에 서버 확장을 할 때에도 매우 쉽게 할 수 있습니다.(하지만, redis clustering 조회해보고 있는데 힘들어보이는.....)

소스는 github에.......

https://github.com/mudchobo/nodejs-socketio_redis_store

 
Posted by 머드초보
,