Nodejs如何使用socket.io做到上线提醒?
Nodejs如何使用socket.io做到上线提醒?
场景如下: 1万个用户,分布在不同的node.js server中,当你登录时,如何通知到你的好友,你已经上线?
我的问题是: 1.如何知道你的好友已经上线? 2.如何发送消息给你的好友(他可能在另外的server中)?
Nodejs如何使用socket.io做到上线提醒?
场景描述
假设有一个应用场景,有1万个用户分布在多个Node.js服务器上。当一个用户登录时,需要通知其好友,该用户已上线。这个问题主要涉及两个方面:
- 如何知道你的好友已经上线?
- 如何将上线通知发送给你的所有好友(他们可能分布在不同的服务器上)?
解决方案
为了解决上述问题,我们可以使用Socket.IO来实现实时通信。Socket.IO是一个可以在浏览器和服务器之间实现实时、双向通信的库。
1. 使用Redis进行消息传递
由于用户分布在不同的Node.js服务器上,我们需要一个中间件来协调消息传递。这里我们使用Redis作为消息队列,因为它支持发布/订阅模式。
2. 实现步骤
-
安装依赖:
npm install express socket.io redis
-
设置Redis客户端:
const redis = require('redis'); const subscriber = redis.createClient(); const publisher = redis.createClient();
-
创建Socket.IO服务器:
const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const app = express(); const server = http.createServer(app); const io = socketIo(server); io.on('connection', (socket) => { console.log('a user connected:', socket.id); // 当用户上线时,订阅他们的频道 socket.on('login', (userId) => { socket.join(userId); // 将用户加入到对应的频道 console.log(`${userId} has logged in`); }); // 处理上线通知 socket.on('onlineNotification', (data) => { const { userId, friendId } = data; io.to(friendId).emit('onlineNotification', { userId }); }); }); server.listen(3000, () => { console.log('listening on *:3000'); });
-
处理好友上线通知:
subscriber.on('message', (channel, message) => { const data = JSON.parse(message); io.to(data.friendId).emit('onlineNotification', data); }); subscriber.subscribe('onlineChannel');
-
前端代码示例:
<script src="/socket.io/socket.io.js"></script> <script> const socket = io(); // 用户登录时调用 function login(userId) { socket.emit('login', userId); } // 发送上线通知 function sendOnlineNotification(userId, friendId) { socket.emit('onlineNotification', { userId, friendId }); } </script>
通过上述步骤,我们可以实现用户登录时的实时通知功能。每个用户登录后会进入一个特定的频道,而其他用户可以通过订阅这些频道来接收上线通知。这样即使用户分布在不同的服务器上,也可以实现高效的实时通信。
结构不是问题,现在的问题是 效率的问题。
现在大部分的例子都是在单机的情况下,当有用户上线时,直接io.sockets.broadcast群发一个消息,但这是个广播消息,效率太低,特别是 在使用 socket.io + redis pub/sub的情况下,一个用户的上下线,就给整个系统是群发一个消息,真是个悲剧.
我自己的想法是 用redis或memerycache 缓存用户的好友列表和全系统的在线用户信息(在线用户信息中包含当前所在server的信息,如IP地址),如果用户的好友列表中有用户在线,则可以找到该在线好友所在的socket.io server的IP地址,将上线消息emit到好友所在的server,好友所在的server收到消息之后,根据该台server中缓存的socket列表,最终可以找到该好友对应的socket连接,并将消息发送出去。
兜了老大的一个圈子,就是为了避免去使去使用全网的broadcast,至于点对点的消息(如:你发送消息给你的好友),这个倒是可以使用 socket.io+redis pub/sub轻松解决,关键是这种通知类的信息比较麻烦,不晓得大家伙有没有更加好的办法?
通知类的消息可以单独系统出来做吧,利用消息队列来下发信息,这样对原系统就影响不大了吧。
在楼主好友上线这个例子,没有必要全网的broadcast。但一个用户上线后,只要查找该用户已经上线的好友,通知这些好友用户就可以了。
比如用 rabbitMQ 之类的异步消息系统。nodejs 有 rabbitMQ 的模块。
请问这个最后是怎么解决的呢?我们也遇到了相同的问题,求解!是采用这个方法吗? “用redis或memerycache 缓存用户的好友列表和全系统的在线用户信息(在线用户信息中包含当前所在server的信息,如IP地址),如果用户的好友列表中有用户在线,则可以找到该在线好友所在的socket.io server的IP地址,将上线消息emit到好友所在的server,好友所在的server收到消息之后,根据该台server中缓存的socket列表,最终可以找到该好友对应的socket连接,并将消息发送出去。”
socket.io还在研究中,不过还是觉得查找好友进行通知比较好,lz说的方案太绕了
楼主可以研究下Meteor。
jljlkj
yyyy
看看网易那个pomelo聊天的例子吧,客户端连接存到session里面,有新用户登录时候就给所有session发送消息就行了。
为了实现一个基于Node.js和socket.io的系统来实现实时上线提醒功能,我们需要确保所有服务器能够互相通信,并且可以识别出特定用户的上线事件。这里是一种可能的解决方案:
服务器端代码
首先,确保每个Node.js服务器实例都安装了socket.io,并能够通过某种方式(如redis或mqtt)进行相互之间的通信。
步骤1: 设置全局事件总线
我们可以使用redis作为消息队列,或者选择其他合适的消息代理(如mqtt、rabbitmq等)。这样可以在多个Node.js服务器之间共享实时状态。
npm install socket.io redis
步骤2: 创建Socket.IO服务
const io = require('socket.io')(httpServer, {
cors: {
origin: '*',
}
});
const redisClient = require('redis').createClient();
io.on('connection', (socket) => {
console.log('User connected');
socket.on('register', (userId) => {
// 将用户加入到在线列表
redisClient.sadd('online-users', userId);
});
socket.on('disconnect', () => {
// 用户断开连接时,从在线列表中移除
redisClient.smembers('online-users', (err, users) => {
if (users && users.includes(socket.userId)) {
redisClient.srem('online-users', socket.userId);
}
});
});
});
步骤3: 发送上线通知
当用户上线时,发送一个通知给其好友:
socket.on('login', (userId) => {
// 当用户上线时,通知所有好友
redisClient.smembers(`friends:${userId}`, (err, friends) => {
friends.forEach(friendId => {
redisClient.get(`socketId:${friendId}`, (err, socketId) => {
if(socketId) {
io.to(socketId).emit('newOnlineUser', { userId });
}
});
});
});
});
客户端代码
客户端需要在成功登录后向服务器发送登录事件,并监听上线通知。
const socket = io();
// 登录成功后
socket.emit('login', userId);
// 监听好友上线通知
socket.on('newOnlineUser', (data) => {
console.log(`${data.userId} is online`);
});
以上代码展示了如何通过Redis来管理在线状态,并在用户上线时通知其好友。这种方式可以扩展到更大规模的应用中。