SignalR 默认连接数上不去是因服务器配置和底层限制所致,需调优Kestrel连接数、HTTP/2、TLS及Hub广播机制,并优化客户端重连策略。

SignalR 默认连接数为什么上不去?
SignalR 在 IIS 或 Kestrel 上默认受限于服务器配置和 ASP.NET Core 的底层限制,不是代码写得“高级”就能突破的。常见现象是:压测时连接数卡在几百就报 ConnectionRefused、TimeoutException 或客户端反复重连失败——这往往不是 SignalR 自身问题,而是 Windows 句柄、线程池、HTTP/2 流控或 TLS 握手堆积导致的。
- IIS 默认
maxConcurrentRequestsPerCPU是 5000,但实际受applicationPool → queueLength(默认1000)和系统MaxUserPort(默认5000)拖累 - Kestrel 默认不启用 HTTP/2,而大量短连接 + 频繁重连会快速耗尽端口和 TLS session cache
-
HubLifetimeManager默认用内存实现,高并发下广播操作易成瓶颈,尤其Clients.All.SendAsync会同步阻塞整个 Hub 线程
必须改的 Kestrel 和 ASP.NET Core 配置项
别只盯着 Hub 类写法,真正起效的是宿主层调优。以下配置需在 Program.cs 中显式设置:
var builder = WebApplication.CreateBuilder(args);
// 关键:Kestrel 连接与缓冲调优
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxConcurrentConnections = 100_000; // 显式放开
serverOptions.Limits.MaxConcurrentUpgradedConnections = 100_000;
serverOptions.Limits.MaxRequestBodySize = null; // 若不用上传
serverOptions.Limits.MinRequestBodyDataRate = null; // 防止慢速攻击误杀
serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(5);
});
// 关键:ASP.NET Core 主机级并发控制
builder.Services.Configure(options =>
{
options.AutomaticAuthentication = false;
options.AuthenticationDisplayName = "Windows Authentication";
});
builder.Services.Configure(options =>
{
options.MaxConnections = 100_000;
options.MaxRequestBodySize = null;
});
var app = builder.Build();
注意:MaxConcurrentConnections 必须设为 null 或足够大数值,否则 Kestrel 内部连接队列会直接拒绝新连接;MaxConcurrentUpgradedConnections 是 WebSocket 升级专用阈值,SignalR 默认走 WebSocket,此项常被忽略。
Hub 层避免广播阻塞的实操方式
高频消息场景下,Clients.All.SendAsync 是性能杀手——它会把所有连接序列化一次、再逐个 await 发送,中间任意一个慢连接都会拖垮整批。替代方案不是“优化序列化”,而是绕过同步广播模型:
- 用
Clients.AllExcept(...).SendAsync减少目标集,但本质未解耦 - 改用
ITransportHeartbeat+ 自定义IHubLifetimeManager实现异步批量投递(推荐) - 更轻量做法:在 Hub 外启动独立后台服务(
BackgroundService),将消息入队(如Channel),由消费者线程池分片推送 - 禁用自动 JSON 序列化缓存:
AddJsonProtocol(opt => opt.PayloadSerializerOptions.WriteIndented = false),减小 GC 压力
示例:用 Channel 实现非阻塞广播
public class BroadcastService : BackgroundService
{
private readonly ChannelReader
客户端连接复用与重连策略关键点
服务端再强,客户端乱建连也会拖垮整体。SignalR JS 客户端默认重试逻辑极易引发雪崩:
- 禁用默认自动重连:
new HubConnectionBuilder().withUrl(...).configureLogging(LogLevel.None).build(),手动控制重连间隔 - 连接前检查
navigator.onLine,避免离线时疯狂重试 - 服务端返回 429 时,客户端应退避重连(如指数退避),而非立即重试
- Web Worker 中运行 SignalR 连接,防止 UI 线程阻塞影响心跳上报
- .NET 客户端务必设置
HubConnectionBuilder.WithAutomaticReconnect()并传入自定义策略,避免RetryExponential默认最大间隔仅 30 秒
最容易被忽略的是:WebSocket 连接本身不感知网络闪断,依赖心跳超时(默认 30 秒)才发现断连。生产环境建议将 KeepAliveInterval 设为 10–15 秒,ClientTimeoutInterval 设为 30–45 秒,并在 OnCloseAsync 中清理资源。











