You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Socket.IO首次emit后无法触发后续emit(执行顺序影响结果)

环境

后端

  • node: latest
  • socket.io | 4.5.2

前端

  • React Native | 0.70.4
  • socket.io-client | 4.6.0
  • 适配Android和iOS

Node.js入口文件

const numCPUs = cpus().length
if (cluster.isPrimary) {
  const app = express()
  const httpServer = http.createServer(app)

  setupMaster(httpServer, { loadBalancingMethod: 'least-connection' })

  setupPrimary()

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork()
  }

  cluster.on('exit', (worker) => {
    cluster.fork()
  })
} else {
  const app = express()
  const httpServer = http.createServer(app)
  const io = new Server(httpServer, { maxHttpBufferSize: 1e8 })

  io.adapter(createAdapter())
  setupWorker(io)

  API.Socket.init(io, process.pid)

  middlewares.forEach((middleware: any) => app.use(middleware))
  routes.forEach((route) => app.use(route.path, route.handler))

  httpServer.listen(CONFIG.PORT, () => {})
}

Socket.IO推送函数实现

sendNewNotification(
  notification: BE.Entities.TNotification,
  toUser: string,
) {
  this.io
    ?.to(toUser)
    .volatile.emit(ECustomEvents.NewNotification, notification)
}

sendPrivateMessage(
  toUser: string | Array<string>,
  chatMessage: BE.Entities.TChatMessage,
  sourceUser: BE.Entities.TUser,
) {
  this.io
    ?.to(toUser)
    .volatile.emit(ECustomEvents.PrivateMessage, chatMessage, sourceUser)
}

问题现象

按以下顺序调用时,目标用户B能收到通知,但无法收到新聊天消息:

API.Socket.sendPrivateMessage(targetUserId, newChatMessage, userToPass)
API.Socket.sendNewNotification(savedNotification, targetUserId)

调换调用顺序后,目标用户B可以正常收到通知和新聊天消息:

API.Socket.sendNewNotification(savedNotification, targetUserId)
API.Socket.sendPrivateMessage(targetUserId, newChatMessage, userToPass)

请问这种情况为何会发生?可能的问题点是什么?


问题原因分析

  1. Volatile消息的特性限制
    你使用的volatile.emit属于无可靠性保障的消息发送方式:它不会存储消息,也不会重试发送。如果发送时目标用户的Socket房间信息未同步到当前Worker进程,消息会直接丢失,不会有任何补发机制。

  2. 集群环境下的房间同步延迟
    你的服务采用Node.js Cluster多进程模式,通过Socket.IO适配器实现跨进程事件同步。当用户B的Socket连接到某Worker进程并加入toUser对应房间后,房间成员信息需要同步到所有Worker节点,这个过程存在延迟。

  • 先调用sendPrivateMessage时,处理请求的Worker可能还未收到用户B的房间同步信息,此时to(toUser).volatile.emit找不到目标Socket,导致消息丢失;后续调用sendNewNotification时,房间信息已同步完成,所以通知能正常送达。
  • 调换顺序后,sendNewNotification的触发会加速房间成员信息的同步,后续调用sendPrivateMessage时,当前Worker已能获取到用户B的房间信息,消息即可正常推送。
  1. 版本兼容性隐患
    Socket.IO服务端(4.5.2)与客户端(4.6.0)存在小版本差异,虽官方声称跨小版本兼容,但底层房间同步或消息处理逻辑的细微不一致,可能加剧消息丢失的概率。

解决方案建议

  • 若消息可靠性要求高,替换volatile.emit为普通emit,普通消息会加入重发队列,直到客户端确认接收。
  • 统一Socket.IO服务端与客户端版本,消除版本差异带来的潜在问题。
  • 发送私有消息前,可通过io.in(toUser).allSockets()检查目标用户的在线Socket状态,确认存在后再执行发送操作。
  • 针对Cluster环境,优化适配器同步策略(比如调整Redis适配器的同步频率,若使用Redis适配器),减少房间信息同步延迟。

内容的提问来源于stack exchange,提问作者spatak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:35:15