Kestrel 生产部署需显式配置监听地址、连接数限制及超时参数:用 ConfigureKestrel 显式绑定 IP 和端口(如 ListenAnyIP(8080)),设 MaxConcurrentConnections(如 10000)和 MaxConcurrentUpgradedConnections(如 5000),调优 KeepAliveTimeout(建议90秒)与 RequestHeadersTimeout(建议15秒),并验证系统级限制(如 somaxconn、file-max)。

Kestrel 默认不限制最大连接数,但不显式配置 MaxConcurrentConnections 和 MaxConcurrentUpgradedConnections,实际并发能力会被系统级限制(如文件描述符、端口耗尽、TLS session cache)卡死,压测时连接数常卡在几百就失败。
如何设置 Kestrel 监听端口与 IP 地址
别依赖默认绑定(http://localhost:5000),生产环境必须显式指定监听地址和端口,否则可能因 IPv6/IPv4 混合绑定失败,或被防火墙拦截。
- 用
ConfigureKestrel+Listen显式绑定:例如options.Listen(IPAddress.Any, 8080)或options.ListenAnyIP(8080) - 若需 HTTPS,必须配
ListenHttps并传入有效证书路径或X509Certificate2 - 避免监听
localhost—— 它只响应本地回环,容器或跨主机调用会直接拒绝 - Linux 上注意
net.core.somaxconn内核参数,若设为 128,而 Kestrel 队列积压超限,新连接会被内核丢弃(无错误日志)
MaxConcurrentConnections 和 MaxConcurrentUpgradedConnections 怎么设
这两个值不设等于“放任自流”,但设错反而更快崩——它们控制的是 Kestrel 内部连接队列的硬闸门,不是线程池或 CPU 能力的映射。
-
MaxConcurrentConnections:纯 HTTP/HTTPS 连接上限。设为null表示不限,但建议根据负载测试结果定值,如10_000;设太小(如 100)会导致新 TCP 握手直接被ConnectionRefused -
MaxConcurrentUpgradedConnections:专管 WebSocket、HTTP/2 Upgrade 后的连接。SignalR 默认走 WebSocket,此项常被忽略,必须单独设高(如5_000),否则客户端反复重连却连不上 - 升级连接不计入
MaxConcurrentConnections,两者是独立计数器,必须都配 - Windows 上还要检查 IIS 的
applicationPool.queueLength(默认 1000),它会在 Kestrel 前拦截请求
为什么调大 ThreadPool.MinThreads 反而更慢
Kestrel 本身不靠线程池处理网络 I/O,它用 epoll / IOCP 异步等待;线程池只负责回调执行和同步上下文任务。盲目调大 ThreadPool.SetMinThreads 不解决瓶颈,还可能拖垮性能。
- 典型症状是
dotnet-counters显示ThreadPool.QueueLength持续 > 100,但CPU很低、ThreadPool.ThreadCount却很高——说明大量线程在空等或被阻塞 - 真正该查的是代码里有没有
.Wait()、.Result、AllowSynchronousIO = true,这些会让线程池线程卡住读取 Request.Body - Linux 上优先调
/proc/sys/net/core/somaxconn和fs.file-max,比调线程数见效快得多 - 如果真要调线程池,最小值建议不超过
Environment.ProcessorCount * 4,且必须配合ThreadPool.SetMaxThreads一起调,避免突发扩容抖动
KeepAliveTimeout 和 RequestHeadersTimeout 的实际影响
这两个超时不是“可有可无”的装饰项,它们直接影响连接复用率、端口回收速度和慢速攻击防御效果。
-
KeepAliveTimeout默认 2 分钟,但移动端或 NAT 网关常 60–90 秒断连,设太长会导致连接假活堆积;建议设为TimeSpan.FromMinutes(1.5)或90秒 -
RequestHeadersTimeout默认 30 秒,对弱网用户不友好;若服务面向公网,降到15秒可更快释放被慢速客户端占着的连接槽位 - HTTP/2 场景下,
KeepAliveTimeout会影响PING帧频率和流控窗口刷新,过短易触发客户端重连 - 不要设
0或TimeSpan.MaxValue—— 这会让连接永远不释放,最终耗尽ulimit -n
最常被跳过的一步是验证:改完配置后,用 ss -s(Linux)或 netstat -an | findstr :8080(Windows)确认 ESTABLISHED 连接数是否真上去了;再用 dotnet-dump ps 查看实际打开的 socket 数,而不是只信日志里的“成功连接”。



















