我在集群 node.js/socket.io/redis pub/sub 应用程序中收到重复消息
Posted
技术标签:
【中文标题】我在集群 node.js/socket.io/redis pub/sub 应用程序中收到重复消息【英文标题】:I'm receiving duplicate messages in my clustered node.js/socket.io/redis pub/sub application 【发布时间】:2012-01-11 21:22:22 【问题描述】:我正在使用 Node.js、带有 Redisstore 的 Socket.io、来自 Socket.io 的集群和 Redis。
我有一个发布/订阅应用程序,它只在一个 Node.js 节点上运行良好。但是,当它承受重负载时,由于 Node.js 不是为多核机器编写的,因此只会最大化服务器的一个核心。
正如您在下面看到的,我现在使用的是 Learnboost 的 Cluster 模块,也就是制作 Socket.io 的人。
但是,当我启动 4 个工作进程时,每个进入并订阅的浏览器客户端都会获得在 Redis 中发布的每条消息的 4 个副本。如果有 3 个工作进程,则有 3 个副本。
我猜我需要以某种方式将 redis pub/sub 功能移动到 cluster.js 文件中。
Cluster.js
var cluster = require('./node_modules/cluster');
cluster('./app')
.set('workers', 4)
.use(cluster.logger('logs'))
.use(cluster.stats())
.use(cluster.pidfiles('pids'))
.use(cluster.cli())
.use(cluster.repl(8888))
.listen(8000);
App.js
redis = require('redis'),
sys = require('sys');
var rc = redis.createClient();
var path = require('path')
, connect = require('connect')
, app = connect.createServer(connect.static(path.join(__dirname, '../')));
// require the new redis store
var sio = require('socket.io')
, RedisStore = sio.RedisStore
, io = sio.listen(app);
io.set('store', new RedisStore);io.sockets.on('connection', function(socket)
sys.log('ShowControl -- Socket connected: ' + socket.id);
socket.on('channel', function(ch)
socket.join(ch)
sys.log('ShowControl -- ' + socket.id + ' joined channel: ' + ch);
);
socket.on('disconnect', function()
console.log('ShowControll -- Socket disconnected: ' + socket.id);
);
);
rc.psubscribe('showcontrol_*');
rc.on('pmessage', function(pat, ch, msg)
io.sockets.in(ch).emit('show_event', msg);
sys.log('ShowControl -- Publish sent to channel: ' + ch);
);
// cluster compatiblity
if (!module.parent)
app.listen(process.argv[2] || 8081);
console.log('Listening on ', app.address());
else
module.exports = app;
client.html
<script src="http://localhost:8000/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<script>
var socket = io.connect('localhost:8000');
socket.emit('channel', 'showcontrol_106');
socket.on('show_event', function (msg)
console.log(msg);
$("body").append('<br/>' + msg);
);
</script>
【问题讨论】:
【参考方案1】:我一直在与 cluster 和 socket.io 作斗争。每次我使用集群功能(虽然我使用内置的 Nodejs 集群)我都会遇到很多性能问题和 socket.io 问题。
在尝试对此进行研究时,我一直在研究 socket.io git 上的错误报告和类似内容,任何使用集群或外部负载平衡器到其服务器的人似乎都遇到了 socket.io 的问题。
似乎会产生“客户端不握手客户端应该重新连接”的问题,如果您增加详细日志记录,您将看到该问题。每当 socket.io 在集群中运行时,这就会出现很多,所以我认为它会恢复到这个状态。即,客户端每次建立新连接时都会连接到 socket.io 集群中的随机实例(授权时会执行多个 http/socket/flash 连接,并且在轮询新数据时会一直执行更多连接)。
现在我已经恢复为一次只使用 1 个 socket.io 进程,这可能是一个错误,但也可能是构建 socket.io 的一个缺点。
补充:我以后解决这个问题的方法是为集群内的每个socket.io实例分配一个唯一的端口,然后在客户端缓存端口选择。
【讨论】:
【参考方案2】:原来这不是 Node.js/Socket.io 的问题,我只是以完全错误的方式处理它。
我不仅从 Node/Socket 堆栈外发布到 Redis 服务器,而且还直接订阅了 Redis 频道。在发布/订阅情况的两端,我绕过了“后端带有 Redis 存储的 Socket.io 集群”。
所以,我创建了一个小应用程序(使用 Node.js/Socket.io/Express),它从我的 Rails 应用程序中获取消息,并使用 socket.io-announce 模块将它们“通知”到 Socket.io 房间。现在,通过使用 Socket.io 路由魔法,每个节点工作人员只能获取和发送消息到直接连接到它们的浏览器。换句话说,由于 pub 和 sub 都发生在 Node.js/Socket.io 堆栈中,因此不再有重复的消息。
清理完我的代码后,我会在某个地方的 github 上放一个示例。
【讨论】:
你可以在这里看到我们做了什么:github.com/StageIt/redis 但是,由于 socket.io 中的错误,我们将其回滚到单个节点。这是对该错误的讨论,现在可能已修复:github.com/LearnBoost/socket.io/issues/438#issuecomment-3371390 上面的 GitHub 链接中没有代码,可以在这里再分享一下吗?我也遇到了同样的问题。以上是关于我在集群 node.js/socket.io/redis pub/sub 应用程序中收到重复消息的主要内容,如果未能解决你的问题,请参考以下文章
当我在给定的 Cassandra 集群中“从 ColumnFamily 中选择 *”时会发生啥
我在Hadoop集群中起集群起不来,看日志说50070端口被占用但是都没有进程啊,而且防火墙也关了。
无法使用来自浏览器的入口访问我在 k8s 集群上的 grafana 仪表板