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

.NET Core SignalR跨服务连接消息延迟问题排查求助

问题排查请求

我有多个基于.NET Core Web API的服务(服务A、服务B)通过SignalR通信:

  • 服务A配置SignalR Hub并接收连接,服务B使用Microsoft.AspNetCore.SignalR.Client包的HubConnection连接服务A,通过hubConnection.On监听消息;
  • 服务B自身也有SignalR Hub,当前约80个网站客户端同时连接;
  • 服务A每分钟向服务B发送约2000条消息,服务B需将消息转发给所有客户端,但服务A发送的消息到达服务B时延迟约2分钟(已记录发送与接收时间差);
  • 本地运行服务B且无客户端连接时无延迟,推测与连接数相关,但80个连接规模不大;
  • 服务B通过反射从BroadcastService类动态订阅方法,收到消息后用Task.Run异步执行处理,在BroadcastService的BroadcastToClients方法中,消息到达此处时已延迟2分钟,该方法会遍历客户端组,通过IHubContext向组内客户端发送消息。

现求助排查此延迟问题的方向。


服务B消息监听代码

hubConnection.On(method.Name, parameterTypes, @params =>
{
    return Task.Run(() =>
    {
        var activity = new Activity("HubListener");
        try
        {
            activity.Start();
            activity.AddTag("TraceId", Guid.NewGuid().ToString());
            activity.AddTag("ArrivalTime", DateTime.UtcNow);

            var obj = _serviceProvider.GetRequiredService(method.DeclaringType);
            method.Invoke(obj, @params);
        }
        catch (Exception e)
        {
            Log.Error(e, "Error In Hub Listener");

        }
        finally
        {
            activity.Stop();
        }
    });
});

BroadcastService类代码

public class BroadcastService {
        private readonly IHubContext<BaseHub> _baseHubContext;

        public BroadcastService(IHubContext<BaseHub> baseHubContext)
        {
            _baseHubContext = baseHubContext ?? BaseHub.CurrentContext;
        }

        public void BroadcastToClients(Message messageFromServiceA)
        {
             // 消息从服务A发出后,大约需要2分钟才能到达此处
             // 客户端按分组划分
             foreach(var group in clientGroups){
                _baseHubContext
                   .Clients
                   .Group(group.Name)
                   .SendAsync("onMessage",messageFromServiceA);
             }
        }
}

排查方向建议
  • 线程池资源耗尽:当前用Task.Run包裹消息处理逻辑,每分钟2000条消息会持续向线程池提交任务。而BroadcastToClients是同步方法,调用SendAsync时未等待完成,会导致线程池线程被长期占用,后续任务排队引发延迟。建议将BroadcastToClients改为异步方法(返回Task并await SendAsync),同时去掉不必要的Task.Run——SignalR的On回调本身支持异步,无需额外包装线程池任务。
  • SignalR传输层积压:检查服务B作为客户端连接服务A时的传输方式,优先使用WebSocket而非长轮询(Long Polling),长轮询本身存在天然延迟。同时查看服务A的SignalR日志,确认消息是否及时发出,服务B的HubConnection是否存在消息缓冲区溢出问题。
  • 组发送的同步阻塞:BroadcastToClients中的foreach循环同步发起大量SendAsync请求,未等待完成,会导致I/O任务堆积。改为循环内await SendAsync,或批量处理组发送请求,避免一次性占用过多I/O资源。
  • 线程池扩容不及时:.NET线程池默认扩容速度较慢,当短时间内有大量任务涌入时,会出现排队等待。可以通过ThreadPool.GetMinThreads查看当前配置,必要时调用ThreadPool.SetMinThreads调整最小工作线程数,减少扩容等待时间。
  • 反射调用的累积开销:每次收到消息都通过GetRequiredService获取实例并反射调用方法,每分钟2000次的频率会累积性能损耗。建议提前缓存服务实例和方法委托,避免重复反射操作。
  • 精准定位延迟阶段:在服务A发送消息时记录时间戳,在服务B的hubConnection.On回调最开始(进入Task.Run之前)就记录接收时间戳,对比两者的时间差,确认延迟是发生在SignalR传输阶段还是后续业务处理阶段。同时监控服务B的线程池队列长度、CPU使用率、内存占用,排查是否存在资源瓶颈。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:31:14