c# Kestrel 和 IIS 在处理高并发请求时的模型区别

来源:这里教程网 时间:2026-02-21 17:36:08 作者:

Kestrel 是纯异步 I/O 模型,IIS 是进程/线程代理模型

Kestrel 本身不创建新线程处理每个请求,而是依赖

ThreadPool
中的少量线程配合操作系统级别的异步 I/O(如 Linux 的 epoll、Windows 的 IOCP)完成大量并发连接的调度。它默认以单个
WebHost
实例运行,所有请求共享同一个事件循环上下文。

IIS 不直接执行 .NET 应用代码,它作为反向代理和进程管理器:接收 HTTP 请求 → 转发给后端的

w3wp.exe
进程(或
dotnet.exe
,取决于托管模型)→ 由 ASP.NET Core 的
WebHost
处理。这意味着请求路径多了一层转发,也引入了额外缓冲和超时配置(如
requestTimeout
maxAllowedContentLength
)。

IIS 的
applicationPool
设置(如“启动模式=始终运行”、“空闲超时=0”)直接影响首次请求延迟和长连接保持能力
Kestrel 在 Linux 上可直连负载均衡器(如 Nginx),绕过 IIS;在 Windows 上若启用
InProcess
托管,IIS 仅做进程宿主,不参与 HTTP 解析
高并发下,IIS 默认的
maxConcurrentRequestsPerCPU
(旧版)或
dynamicTypes
配置可能成为瓶颈,而 Kestrel 的吞吐更取决于 CPU 和内存带宽,而非线程数

Kestrel 的连接队列和请求超时由代码层控制,IIS 有独立的中间层超时

Kestrel 的行为由

KestrelServerOptions
直接控制,比如
LimitMaxConcurrentConnections
KeepAliveTimeout
RequestHeadersTimeout
等,这些都在应用启动时通过
ConfigureKestrel
设置。它们作用于原始 TCP 连接和 HTTP 头解析阶段,不经过任何中间代理。

IIS 则在多个层级叠加超时:客户端到 IIS 的

connectionTimeout
system.webServer/serverRuntime
)、IIS 到后端的
requestTimeout
aspNetCore
元素)、以及后端 Kestrel 自身的超时。三者不一致时,最短的那个会先触发中断,且错误日志分散在不同位置(
eventvwr
stdoutLogEnabled
Application Insights
)。

常见现象:
502.3 Bad Gateway
往往是 IIS 等待后端响应超时,但 Kestrel 其实还在处理;此时需比对
aspNetCore/@requestTimeout
Kestrel.RequestHeadersTimeout
Linux + Kestrel 场景下,
SO_BACKLOG
(即
ListenBacklog
)设太小会导致 SYN 包被丢弃,表现为连接拒绝而非超时——这在 IIS 下不会出现,因为 IIS 自己管理 accept 队列
KestrelServerOptions.Limits.MaxRequestBodySize
默认为
30MB
,而 IIS 的
maxAllowedContentLength
默认仅
30MB
(但单位是字节),数值相同却易因单位混淆导致上传失败

并发压测时线程池饥饿表现完全不同

当应用中存在同步阻塞调用(如

Task.Wait()
Thread.Sleep()
、未 await 的
HttpClient
调用),Kestrel 的线程池线程会被快速耗尽,后续请求在
ThreadPool
队列中等待,表现为延迟陡增、
ThreadPool.GetAvailableThreads
返回值趋近于 0。此时 CPU 使用率可能并不高,但吞吐崩溃。

IIS 在

OutOfProcess
模式下,w3wp 进程有自己的线程池,与 Kestrel 所在的
dotnet.exe
进程隔离;而在
InProcess
模式下,两者共用同一进程和线程池,问题表现类似 Kestrel 单独运行,但多了 IIS 的健康检查重试逻辑(如自动重启卡死的 w3wp)。

诊断方法:
dotnet-dump collect -p <pid></pid>
+
dumpheap -stat
查看
WaitHandle
ManualResetEvent
实例是否异常增多
IIS 的
applicationPool/@queueLength
是关键指标:超过默认 1000 表示请求在 IIS 层已排队,说明后端处理不过来或网络转发延迟大
Kestrel 日志中出现大量
Connection id "..." request timed out
,但没有对应的应用层异常,大概率是底层 I/O 超时而非业务卡顿
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 1000;
    serverOptions.Limits.MaxRequestBodySize = 100 * 1024 * 1024; // 100MB
    serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
});

真正难调的不是参数本身,而是 IIS 和 Kestrel 之间那几毫秒的转发延迟、缓冲区大小差异、以及超时链路里任意一环的静默截断。线上遇到偶发 502 或连接重置,先确认哪一层先超时,再查对应日志——别只盯着

Program.cs
里的配置。

相关推荐

热文推荐