不使用第三方推送服务 基于Node.js Socket.io开发推送功能咨询
可行性结论
你提出的两个核心需求完全可以基于现有技术栈实现,无需依赖任何第三方推送服务商。
核心功能实现逻辑
1. 向指定单个或多个用户推送通知
这是Socket.io的原生支持能力,落地步骤如下:
- 建立用户ID与Socket连接ID的映射关系:单机部署可以用内存
Map存储,分布式部署可以用Redis存储,结构为{ userId: [socketId1, socketId2...] },支持同一个用户多端同时在线绑定多个连接 - 连接时做身份校验:用户建立Socket连接时需要携带JWT等身份凭证,服务端校验通过后绑定用户ID和当前Socket ID,示例代码如下:
// Socket.io服务端连接逻辑 io.on('connection', async (socket) => { // 从连接参数中获取身份令牌 const token = socket.handshake.auth.token; const userId = verifyJWT(token); // 替换为你的JWT校验逻辑 if (!userId) { socket.disconnect(); return; } // 存储用户ID和Socket ID的映射 await redis.sadd(`user:${userId}:sockets`, socket.id); // 断开连接时清理映射 socket.on('disconnect', async () => { await redis.srem(`user:${userId}:sockets`, socket.id); }); });
- 封装推送方法:推送时遍历目标用户的所有有效Socket ID发送消息即可,同时支持单个和批量用户推送:
// 单个用户推送 async function pushToSingleUser(userId, notificationPayload) { const socketIds = await redis.smembers(`user:${userId}:sockets`); socketIds.forEach(socketId => { io.to(socketId).emit('notification', notificationPayload); }); } // 批量用户推送 async function pushToBatchUsers(userIds, notificationPayload) { for (const userId of userIds) { await pushToSingleUser(userId, notificationPayload); } }
2. Socket断开时向后台运行用户推送通知
该需求需要分Web端和安卓端分别适配:
Web端(基于Service Worker)
- 前端先注册Service Worker,向用户申请系统通知权限,授权后获取Web Push标准的订阅对象,将订阅对象与对应用户ID绑定存储到服务端数据库
- 服务端推送前先检查目标用户是否有活跃的Socket连接,如果没有活跃连接,就用生成的VAPID私钥签名推送内容,通过Web Push协议发送到用户的订阅端点,Service Worker接收到推送后会自动唤起系统通知,无论浏览器在后台运行还是标签页关闭都能正常接收
- 额外补充离线消息兜底:所有推送消息先持久化到数据库标记推送状态,用户重新建立Socket连接时自动拉取所有未读的离线消息补推,避免推送丢失
安卓端
- 可自行实现原生层长连接保活逻辑,或者采用WorkManager做定期离线消息拉取,不需要依赖FCM
- 注意国内安卓厂商的后台权限限制,落地时需要引导用户给应用开启自启动、后台活动权限,避免后台进程被系统杀掉导致收不到推送
优化建议
- 给所有推送消息添加唯一ID,客户端收到消息后回传ACK确认,服务端收到ACK后标记消息为已送达,未收到ACK的消息自动走重推或离线推送逻辑
- 分布式部署Socket.io服务时需要配置Redis适配器,保证跨节点的消息可以正常投递
内容的提问来源于stack exchange,提问作者Barsian
相关产品推荐
相关产品推荐

