如何在socket.io中重用redis连接?

Posted

技术标签:

【中文标题】如何在socket.io中重用redis连接?【英文标题】:How to reuse redis connection in socket.io? 【发布时间】:2011-08-10 00:12:09 【问题描述】:

这是我的代码,使用 socket.io 作为 WebSocket,后端使用 pub/sub redis。

var io = io.listen(server),
    buffer = [];

var redis = require("redis");

var subscribe = redis.createClient();  **<--- open new connection overhead**

io.on('connection', function(client) 

    console.log(client.request.headers.cookie);

    subscribe.get("..", function (err, replies) 

    );

    subscribe.on("message",function(channel,message) 

        var msg =  message: [client.sessionId, message] ;
        buffer.push(msg);
        if (buffer.length > 15) buffer.shift();
        client.send(msg);
    );

    client.on('message', function(message)
    );

    client.on('disconnect', function()
        subscribe.quit();
    );
);

每个新的 io 请求都会创建新的 redis 连接。如果有人打开带有 100 个标签的浏览器,那么 redis 客户端将打开 100 个连接。不好看。

如果cookie相同,是否可以重用redis连接? 因此,如果有人打开许多浏览器选项卡也视为打开 1 个连接。

【问题讨论】:

我刚刚写了一个scalable socket.io sample你不妨看看。 这是一个不错的link 【参考方案1】:

实际上,如果您在“连接”事件上实例化客户端,那么您只是为每个连接创建一个新的 redis 客户端。我在创建聊天系统时更喜欢做的是创建三个 redis 客户端。一种用于发布、订阅,另一种用于将值存储到 redis 中。

例如:

var socketio = require("socket.io")
var redis = require("redis")

// redis clients
var store = redis.createClient()
var pub = redis.createClient()
var sub = redis.createClient()

// ... application paths go here

var socket = socketio.listen(app)

sub.subscribe("chat")

socket.on("connection", function(client)
  client.send("welcome!")

  client.on("message", function(text)
    store.incr("messageNextId", function(e, id)
      store.hmset("messages:" + id,  uid: client.sessionId, text: text , function(e, r)
        pub.publish("chat", "messages:" + id)
      )
    )
  )

  client.on("disconnect", function()
    client.broadcast(client.sessionId + " disconnected")
  )

  sub.on("message", function(pattern, key)
    store.hgetall(key, function(e, obj)
      client.send(obj.uid + ": " + obj.text)
    )
  )

)

【讨论】:

要明确一点,不管连接了多少用户,这总共只创建了三个redis客户端。添加另一个节点进程显然会导致更多的redis客户端。 @Noli 好问题。您会注意到,因为我们在套接字“连接”闭包中订阅了一个 redis 通道,所以这就是向每个人发送消息所需的全部内容,因为子对象上的事件“消息”将为每个连接的客户端触发.如果我们使用 client.broadcast() 每个人都会看到消息乘以房间里的人数。 @Noli 澄清一下,我们可以使用广播,但我们必须在“连接”闭包之外绑定侦听器,以便事件只被触发一次。我们还必须将其更改为 socket.broadcast() 因为客户端对象对我们不可用。根据情况,这可能会更好。很好的收获:) 我知道这里有很大的时间差距,但我相信这个问题的答案有些危险。每次有新客户端加入聊天时,都会将事件侦听器绑定到 sub 的消息事件。客户端断开连接后,此事件侦听器将保留。这将导致为已经离开的客户处理消息的陈旧事件侦听器的积累。 @tabdulla 我知道这个响应更晚了,但我已经想出了如何解决悬空监听器问题。 ***.com/questions/11617811/…【参考方案2】:

Redis 是 optimized for a high level of concurrent connections。在node_redis模块中还有discussion关于多数据库连接和连接池的实现。

是否可以重用redis 如果 cookie 相同,则连接?所以 如果有人也打开许多浏览器标签 视为打开 1 个连接。

您可以在客户端使用例如html5 storage 来保持主动连接只有一个选项卡,其他选项卡将通过存储事件处理通信/消息。与this问题有关。

【讨论】:

我认为可以将redis客户端存储到sessioncookie中。因此,当下次使用相同的 cookie 调用时,可以重用 redis 连接并发布消息。例如。 var sessioncookie = redis.createClient();所以最好在服务器站点中进行。【参考方案3】:

我遇到了这个确切的问题,有一个额外的要求,即客户端必须能够订阅私人频道,并且不应将发布到这些频道的内容发送给所有听众。我试图通过编写一个微型插件来解决这个问题。插件:

仅使用 2 个 redis 连接,一个用于 pub,一个用于 sub 总共只订阅“消息”一次(不是每个 redis 连接一次) 允许客户端订阅他们自己的私人频道,而不会将消息发送给所有其他监听客户端。

如果您在有 redis 连接限制的地方进行原型设计(例如 redis-to-go),则特别有用。 SO链接:https://***.com/a/16770510/685404

【讨论】:

主要问题是redis会为每个订阅频道的客户端吃掉内存。如果有 50k 并发用户,那么实现这个不是一个好主意。 非常好,在我的例子中是为了一个游戏,一些客户端数据需要发送给组,而其他的只发送给单个玩家。但我想它不会被 50k 用户批准^^【参考方案4】:

客户端断开连接时需要移除监听器。

var io = io.listen(server),
    buffer = [];

var redis = require("redis");

var subscribe = redis.createClient();  

io.on('connection', function(client) 

    console.log(client.request.headers.cookie);

    subscribe.get("..", function (err, replies) 

    );

    var redis_handler = function(channel,message) 

        var msg =  message: [client.sessionId, message] ;
        buffer.push(msg);
        if (buffer.length > 15) buffer.shift();
        client.send(msg);
    ;

    subscribe.on("message", redis_handler);


    client.on('message', function(message)
    );

    client.on('disconnect', function()
        subscribe.removeListerner('message', redis_handler)
        //subscribe.quit();
    );
);

见Redis, Node.js, and Socket.io : Cross server authentication and node.js understanding

【讨论】:

【参考方案5】:

自从提出/回答了这个问题以来,使用 redis 作为存储变得更加简单。 It is built in now.

注意,如果您使用 redis 是因为您使用的是新的节点集群功能(使用多个 CPU),您必须创建服务器并在每个集群分叉内附加监听器(这实际上从未在任何地方解释过在任何文档中;))。我在网上找到的唯一一个很好的代码示例是用 CoffeeScript 编写的,我看到很多人说这种类型的东西“根本行不通”,如果你做错了,它肯定不会时间>。 Here's an example of "doing it right" (but it is in CoffeeScript)

【讨论】:

以上是关于如何在socket.io中重用redis连接?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 socket.io-redis 发送私人消息(发射)

如何重用 socket.io 发射功能?试图将套接字发射功能附加到 SwiftUI 按钮(操作)?

node.js + socket.io + redis 架构 - 水平服务器缩放套接字连接?

使用 Socket.io 和 Redis 的节点集群问题

如何在前端获取 socket.on 函数?事件被触发和处理。我正在使用 socket.io、NodeJS 服务器和 Redis.io

在 node.js 中,如何使用 socket.io 和 express 设置 redis?具体使用 RedisStore()