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

ASP.NET Core SignalR应用水平扩展时如何跨实例校验用户连接状态

水平扩展下SignalR用户在线状态校验与脏数据处理方案

核心问题说明

你当前遇到的两个核心问题本质是分布式场景下的状态一致性问题:

  • 静态列表public static readonly List<string> Connected = new();是进程内存储,水平扩容后每个实例独立维护自己的列表,跨实例不可见,必然失效
  • 持久化数据库存储连接映射没有失效机制,服务实例异常宕机时不会触发OnDisconnectedAsync回调,无法主动清理无效连接,产生脏数据

最优解决方案(基于已有的Redis背板实现,无需额外引入组件)

因为你已经用Redis做SignalR背板,直接复用该Redis实例实现带自动过期的连接状态存储即可:

  • 存储设计
    • 每个连接对应Redis字符串键:signalr:conn:{connectionId},值存储格式为{用户ID}|{当前服务实例唯一标识},设置90秒滑动过期时间(为SignalR默认30秒KeepAlive间隔的3倍,可根据自己的KeepAlive配置调整为2~3倍)
    • 每个用户对应Redis集合键:signalr:user:{userId}:conns,存储该用户所有有效连接ID
  • 回调逻辑改造
    • 重写OnConnectedAsync:用户连接成功时,写入上述两个Redis键,将当前连接ID加入对应用户的连接集合
    • 重写OnDisconnectedAsync:用户正常断开时,从用户连接集合中移除当前连接ID,同时删除对应连接的Redis键
  • 续期逻辑实现
    每个SignalR服务实例启动一个后台定时任务,间隔30秒遍历当前实例持有的所有有效SignalR连接,批量给这些连接对应的signalr:conn:{connectionId}键续期90秒
  • 脏数据自动清理
    服务实例异常宕机后,该实例持有的所有连接对应的Redis键不会被续期,90秒后自动过期失效。每次查询用户在线状态前,先清理该用户连接集合中对应的signalr:conn:{connectionId}已不存在的无效连接ID即可

在线状态校验逻辑

校验用户是否在线时,查询对应signalr:user:{userId}:conns集合的有效连接数,只要存在至少1个有效连接,即可判定为在线。
如果需要极高的状态准确性,可在查询时增加主动探测逻辑:向该用户的所有有效连接发送心跳探测消息,客户端收到后立即回包,10秒内未收到回包的连接直接判定为失效并清理。

兜底方案(基于数据库存储的适配改造)

如果你必须使用数据库存储连接映射,可按如下逻辑改造避免脏数据:

  • 给连接映射表增加last_heartbeat(最后心跳时间)、server_instance_id(所属服务实例标识)字段
  • 每个服务实例每隔30秒批量更新自身所有有效连接的last_heartbeat为当前时间
  • 部署全局定时任务,每隔1分钟删除所有last_heartbeat超过90秒的记录即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 13:15:03