.NET SignalR Hub持续被释放,服务端向客户端推送失败求助
SignalR Hub持续被释放导致服务端无法向客户端通信问题分析
问题现象
- 配置.NET SignalR内部Hub,仅允许桌面应用连接
- 客户端可正常向Hub发起调用,但Hub向客户端推送消息时提示「已释放」错误
- Hub实例在连接建立后持续触发Dispose操作,客户端未收到断开事件且连接状态显示正常
- Hub析构函数从未被调用
已排查信息
- 仅发起1次连接请求,客户端连接状态显示正常
- 核心异常点:Hub实例频繁被Dispose,导致服务端无法持有有效连接实例推送消息
服务端Hub配置代码
public void ConfigureServices(IServiceCollection services) { services.AddGrpc(); services.AddCors(o => { o.AddDefaultPolicy(b => { b.WithOrigins(TargetURL) .AllowCredentials() .AllowAnyHeader(); }); }); services.AddHostedService<Worker>(); services.AddSignalR(hubOptions => { hubOptions.EnableDetailedErrors = true; hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(100); hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(100); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "oiStockPusherSite v1")); } app.UseRouting(); app.UseCors(PolicyName); app.UseEndpoints(endpoints => { //endpoints.MapControllers(); endpoints.MapHub<TradingServiceHub>("/orderITSignalR", options => { options.Transports = HttpTransportType.LongPolling; // you may also need this }); }); }
客户端Hub激活代码
protected virtual bool PrepareTradeActivityHub(string TargetHub) { string TradeActivitySignalRURL = ""; bool Res = false; lock (PreparingHub) { TradeActivitySignalRURL = Connection; if (!PrepareNewHub) { if (HubConnectionOld == null) { EventLog.WriteEntry("Support", string.Format("Prepare Trade Service Hub for {0}", TradeActivitySignalRURL), EventLogEntryType.Information); HubConnectionOld = new Microsoft.AspNet.SignalR.Client.HubConnection(TradeActivitySignalRURL); HubConnectionOld.Closed += TradingClientHubConnection_Closed; ClientHubProxy = HubConnectionOld.CreateHubProxy(TargetHub); Task<bool> Result = TradingClientHubConnection_Connect(); Result.Wait(); if (!Result.Result) { EventLog.WriteEntry("Support", "Failed to start Trade Service Hub", EventLogEntryType.Error); StopHubConnection(); } else { EventLog.WriteEntry("Support", "Connected to Trade Service Hub", EventLogEntryType.Error); Result = ClientHubProxy?.Invoke<bool>("Active"); if (Result != null) { Result.Wait(); Res = Result.Result; CallCheckActivated(); HubConnected = true; } else Res = false; } } else { EventLog.WriteEntry("Support", "Check if Hub still connectedHub", EventLogEntryType.Information); Res = true; if (ClientHubProxy != null) { Task.Run(async () => { if (ClientHubProxy != null) { EventLog.WriteEntry("Support", "Test calling Hub Active operation", EventLogEntryType.Information); Res = await ClientHubProxy?.Invoke<bool>("Active"); if (Res) { CallCheckActivated(); EventLog.WriteEntry("Support", "Successfully called Hub Active operation", EventLogEntryType.Information); } else { EventLog.WriteEntry("Support", "Problem calling Hub Active operation", EventLogEntryType.Error); } } } ).Wait(); } else { EventLog.WriteEntry("Support", "Hub not avaible - should be", EventLogEntryType.Error); } } } else { if (HubConnectionNew == null) { EventLog.WriteEntry("Support", string.Format("Prepare Trade Service Hub for {0}", TradeActivitySignalRURL), EventLogEntryType.Information); HubConnectionNew = new HubConnectionBuilder().WithUrl(TradeActivitySignalRURL).Build(); HubConnectionNew.Closed += StockPusherHubDisconnect; var Connect = StockPusherHubConnection_Connect(); Connect.Wait(); EventLog.WriteEntry("Support", "Test calling Hub Active operation", EventLogEntryType.Information); var Response = HubConnectionNew.InvokeAsync<bool>("Active").Result; if (!Response) { HubConnectionNew = null; EventLog.WriteEntry("Support", "Problem calling Hub Active operation", EventLogEntryType.Error); HubConnected = false; } else { EventLog.WriteEntry("Support", "Successfully called Hub Active operation", EventLogEntryType.Information); Res = true; HubConnected = true; HubConnectionNew.ServerTimeout = TimeSpan.FromSeconds(100000); } } else { EventLog.WriteEntry("Support", "Test calling Hub Active operation", EventLogEntryType.Information); var Response = HubConnectionNew.InvokeAsync<bool>("Active").Result; if (!Response) { HubConnectionNew = null; EventLog.WriteEntry("Support", "Problem calling Hub Active operation", EventLogEntryType.Error); HubConnected = false; } else { EventLog.WriteEntry("Support", "Successfully called Hub Active operation", EventLogEntryType.Information); Res = true; HubConnected = true; } } } } return Res; } protected virtual async Task StockPusherHubConnection_Connect() { Exception exception = null; Exception except = null; try { if (HubConnectionNew != null) { lock (PreparingHub) { EventLog.WriteEntry("Support", string.Format("Attempt to connect to Hub for {0}", Connection), EventLogEntryType.Information); var HubConnect = HubConnectionNew?.StartAsync(); HubConnect?.Wait(); HubConnect?.ContinueWith( task => { try { if (task != null && task.IsFaulted) { EventLog.WriteEntry("Support", "Problem connecting to Hub for", EventLogEntryType.Error); EventLog.WriteEntry("Support", task.Exception.Message, EventLogEntryType.Error); exception = task?.Exception; except = exception?.InnerException; while (except != null) { except = except?.InnerException; if (except != null) EventLog.WriteEntry("Support", except.Message, EventLogEntryType.Error); } } } catch { } } ).Wait(); } } } catch (Exception ex) { } }
问题根源分析及解决方案
1. SignalR版本不兼容
客户端同时混用旧版Microsoft.AspNet.SignalR.Client和ASP.NET Core SignalR的HubConnectionBuilder,导致服务端与客户端连接协议不匹配,服务端无法维持有效连接,频繁释放Hub实例。
- 解决:统一使用ASP.NET Core SignalR客户端库,移除旧版相关代码。
2. 同步阻塞调用引发连接异常
客户端大量使用.Wait()和.Result阻塞异步操作,易造成线程死锁或连接状态异常,服务端因未及时收到客户端心跳判定连接失效,释放Hub实例。
- 解决:将客户端代码改为全异步模式,用
await替代同步阻塞调用。
3. CORS配置不一致
服务端配置了DefaultPolicy但启用时使用PolicyName,可能导致CORS验证失败,服务端主动断开连接释放Hub。
- 解决:确保CORS策略名称一致,或直接使用
app.UseCors()启用默认策略。
4. Hub生命周期管理错误
若服务端Hub存在手动调用Dispose的逻辑,会导致Hub被提前释放。
- 解决:检查Hub类代码,移除手动Dispose逻辑,确保资源释放符合SignalR生命周期要求。
5. 长轮询传输的稳定性问题
仅启用长轮询传输,依赖客户端定期发起请求维持连接,若请求不及时,服务端会释放连接。
- 解决:允许多种传输方式(如
HttpTransportType.WebSockets | HttpTransportType.LongPolling),优先使用WebSocket;同时调整客户端ServerTimeout和服务端ClientTimeoutInterval、KeepAliveInterval参数,确保心跳机制正常运行。
内容的提问来源于stack exchange,提问作者user23478542
相关产品推荐
相关产品推荐

