.Net 6 SignalR 实际业务开发中遇到的问题及解决办法

发布时间 2023-09-01 19:24:13作者: Net开发-孔明

一、SiganlR 使用的协议类型

  • 1.websocket即时通讯协议
  • 2.Server-Sent Events(SSE)服务器事件
  • 3.longpolling 长轮询。

如果客户端开启协商,会按顺序选择可兼容的协议。(默认开启协议协商)
如果客户端禁用协商,只能websocket协议。

二、连接过程

  • 1.http 协议协商过程 客户端向服务器发http请求连接,询问服务器支持什么协议。
    服务端返回支持的协议类型,并分配ConnectionId。(未禁用协议协商时有)
  • 2.ws 客户端确认协议后发送ws请求,建立连接。

三、身份认证

  • websocket协议 无请求报文头headers,token只能放 QueryString 中,一般参数名定为 access_token。
  • 在 Hub中引入身份认证服务,Hub OnConnectedAsync()中,即建立连接时,通过身份认证服务解析token是否合法并拿到用户信息。

四、集群、分布式部署问题

问题1. 多个服务器集群环境,客户端协商过程,可能请求到不同服务器。

在协商过程,如果有AB两个服务器集群
客户端第一次http请求A,A服务器记录了客户端连接信息上下文。
客户端第二次ws连接发送到B服务器,而B服务器没有客户端连接信息上下文,就出错了。

方式1. 粘性会话

网关服务器 或 负载均衡服务器 进行配置,把来自同一个客户端的请求都转发给同一台服务器。

  • 优点:兼容性最好,客户端按兼容性依次选择协议
  • 缺点:公网ip没办法平均分配,很难控制。
方式2. 禁用协商

客户端连接时设置禁用协商,连接时仅一次ws请求。

  • 缺点:只能适用于兼容websocket的浏览器 只能使用websocket协议,无法使用 sse 和 longpolling

问题2. 集群环境 消息不能跨服务器连接发送。

A服务器向全部连接发消息,只能发送到连接A服务器的客户端,不能发送给连接到B服务器的客户端。

  • 解决办法:引入 Microsoft.AspNetCore.SignalR.StackExchangeRedis 包,注入分布式SignalR服务到DI中。
    作用:在多个服务器实例之间共享消息和连接状态。确保跨服务器实例的连接同步和消息传递。
     services.AddSignalR(o =>
      {
          o.EnableDetailedErrors = true;//详细异常信息
          o.HandshakeTimeout = TimeSpan.FromSeconds(30);//握手超时时间
      }).AddStackExchangeRedis("{redis连接字符串}", opt =>
      {
          opt.Configuration.ChannelPrefix = "{RedisKey前缀}";
      });//分布式 SignalR 消息
    

问题3. 在后台任务中使用Hub推送消息。

  • 1.建立连接,从token中拿到用户信息。
    • 缓存: 用户id - [连接id集合]
    • 缓存: 连接id - 用户id
  • 2.断开连接,从缓存中移除 当前连接id
  • 3.后台任务
    • 注入分布式SignalR服务
    • 注入Hub服务
    • 业务中拿到要接收消息的 用户id集合
    • 从缓存中找到对应的 全部连接id
    • 调Hub向连接id推送SignalR消息