如何使用Redis、Express和Socket.io使会话正常工作?

9
所以我正在尝试在socket.on('connection', ...)中使用Sessions。 我正在尝试使用最新版本的模块:Socket.io - 0.9.13,Express - 3.1.0和其他模块的最新版本。
无论如何,我都尝试过使用两个模块 'connect-redis' 和 'session.socket.io',它们都有类似的问题。
在我的代码中,我有两个redis存储(socketio.RedisStore和 require('connect-redis')(express)),现在这个程序都可以正常运行,但是因为express和socket.io需要共享会话数据,所以我想知道这个设置是否会正确地使用sessions?对于express/socketio,会话存储需要是相同的对象吗?对我来说是一个有点模糊的领域,因为两个RedisStore将在后台使用相同的数据库?
我已经尝试在两个位置使用socket.io redisStore或connect-redis redisStore,但socket.io不喜欢connect-redis redisStore,而express不喜欢socketio.redisStore。
如果我使用connect-redis RedisStore,那么socket.io/lib/manager.js会报错: this.store.subscribe(... TypeError Object # has no method 'subscribe'
如果我使用socketio.RedisStore,那么express/node_modules/connect/lib/middleware/session.js会报错: TypeError: Object # has no method 'get'
*注意:我更愿意让session.socket.io插件工作,但是当我使用该插件进行相同的设置时,express也会报错: TypeError: Object # has no method 'get'
所以,我是否可以为会话使用2个不同的RedisStores,或者需要让其中一个适用于两者,并且如果需要的话,有什么解决方法?
我的当前代码如下:
var
    CONST = {
        port: 80,
        sessionKey: 'your secret sauce'
    };

var 
    redis = require('redis');

var 
    express = require('express'),
    socketio = require('socket.io'),
    RedisStore = require('connect-redis')(express);

var 
    redisStore = new RedisStore(),
    socketStore = new socketio.RedisStore();

var 
    app = express(),
    server = require('http').createServer(app),
    io = socketio.listen(server);

app.configure(function(){
    app.use(express.cookieParser( CONST.sessionKey ));
    app.use(express.session({ secret: CONST.sessionKey, store: redisStore }));
    app.use(express.static(__dirname + '/test'));
    app.get('/', function (req, res) {res.sendfile(__dirname + '/test/' + 'index.htm');});
});

io.configure(function(){
    io.set('log level', 1);
    io.enable('browser client minification');
    io.enable('browser client etag');
    io.enable('browser client gzip');
    io.set('store', socketStore);
});

io.sockets.on('connection', function(socket){
    socket.emit('message', 'Test 1 from server')
});

server.listen( CONST.port );

console.log('running...');
2个回答

4

看到你的最后一条笔记(无法使用redis在多个进程之间共享状态),我也遇到了同样的问题并找到了解决方案:

var express = require("express.io");
var swig = require('swig');
var redis = require('redis');
var RedisStore = require('connect-redis')(express);

workers = function() {
    var app = express().http().io();

    app.use(express.cookieParser());
    app.use(express.session({
        secret: 'very cool secretcode',
        store: new RedisStore({ client: redis.createClient() })
    }));

    app.io.set('store', new express.io.RedisStore({
        redisPub: redis.createClient(),
        redisSub: redis.createClient(),
        redisClient: redis.createClient()
    }));


    app.get('/', function(req, res) {
        res.render('index');
    });

    app.listen(3000);


    app.io.route('ready', function(req){
        //setup session stuff, use session stuff, etc.  Or make new routes
    });
};

cluster = require('cluster');
numCPUs = require('os').cpus().length;

if (cluster.isMaster)
{
    for (var i = 0; i < numCPUs; i++)
    {
        cluster.fork();
    }
}
else
{
    workers();
}

关键在于使用 express.io.RedisStore,并将其设置为 app.io.set('store', new express.io.RedisStore({。谢谢! - ruffrey

4
io.configure 中,您需要将套接字与 HTTP 会话链接起来。
这是一段提取 cookie 的代码(使用带有 xhr-polling 的 socket.io,我不知道它是否适用于 websocket,尽管我认为它可以工作)。
var cookie = require('cookie');
var connect = require('connect');

var sessionStore = new RedisStore({
  client: redis // the redis client
});

socketio.set('authorization', function(data, cb) {
  if (data.headers.cookie) {
    var sessionCookie = cookie.parse(data.headers.cookie);
    var sessionID = connect.utils.parseSignedCookie(sessionCookie['connect.sid'], secret);
    sessionStore.get(sessionID, function(err, session) {
      if (err || !session) {
        cb('Error', false);
      } else {
        data.session = session;
        data.sessionID = sessionID;
        cb(null, true);
      }
    });
  } else {
    cb('No cookie', false);
  }
});

然后您可以使用以下方式访问会话:

socket.on("selector", function(data, reply) {
  var session = this.handshake.session;
  ...
}

这样做的另一个好处是,它会检查是否有有效的会话,因此只有已登录的用户可以使用 sockets。当然,您可以使用不同的逻辑。


谢谢您的快速回复,那么如果我想使用session.socket.io插件,这是否是相同的解决方案?还是说那是一个完全不同的问题? - Josh Mc
这不使用session.socket.io插件,适用于最新版本的socket.io和express。 - Pascal Belloncle
看起来是有效的,本来希望使用session.socket.io,但这个也很好用,所以谢谢。 - Josh Mc
注意,为了使它工作,我不得不删除“io.set('store',socketStore);”,这实际上是一个非常不幸的妥协,因为这意味着socket.io将无法使用redis在多个进程之间共享其状态 :( 希望未来我能找到一种方法。 - Josh Mc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接