.Net Core 5 MVC项目Kestrel服务器响应过慢性能问题求助
问题根因定位与排查解决方案
可能的核心原因
- 视图渲染环节的序列化/反射开销:如果视图使用了强类型模型,且模型嵌套层级深、包含大量非基础类型属性,Razor视图渲染时会触发大量反射操作;或是使用
JsonSerializer序列化大对象时没有加优化配置,默认的序列化规则会遍历所有属性包括导航属性,就算未主动使用也会触发隐式加载 - Kestrel响应缓冲配置异常:默认Kestrel会对响应做缓冲,如果缓冲阈值设得比195KB小,会触发多次磁盘IO刷缓冲,尤其是部署在机械硬盘的服务器上开销会被大幅放大
- 未释放的数据库上下文跟踪:虽然数据库查询耗时短,但如果DbContext的ChangeTracker处于开启状态,返回的模型是EF Core直接跟踪的实体对象,视图渲染访问属性时会触发大量跟踪校验甚至延迟加载未加载的导航属性,就算没有写关联查询也可能触发隐式的数据库访问
- Razor视图编译延迟:如果生产环境没有开启预编译视图,第一次访问或者应用回收后访问视图时会触发运行时编译,就算后续访问如果内存不足视图编译缓存被回收也会重复触发编译开销
- 响应压缩未开启:195KB的未压缩负载在传输前如果做gzip/brotli压缩可以降到30KB以内,如果未开压缩,加上弱网环境或者TCP慢启动的开销也可能出现卡顿(如果Chrome网络监控里的TTFB(首字节响应时间) 占了7秒的大部分时长,就是服务端问题,否则是传输/前端渲染问题)
分步排查方法
- 先确认耗时环节归属:查看Chrome网络监控窗口里的TTFB 和内容下载时间占比,如果TTFB超过6.5秒,可确定为服务端问题,否则排查网络或者前端渲染逻辑
- 服务端埋点验证:在Controller的Action入口、数据库查询完成、返回
View()之前三个节点加时间戳埋点,直接输出三个节点的时间差,确认耗时是在Action执行完成到View返回之间,还是Action内部的隐藏开销 - 分析调用栈耗时:查看Controller调用栈截图里各方法的CPU耗时占比,如果是
Microsoft.AspNetCore.Mvc.Razor相关的方法占比最高,就是视图渲染的问题;如果是System.Text.Json相关的就是序列化问题;如果是Microsoft.EntityFrameworkCore相关的就是EF的跟踪或者延迟加载问题 - 替换返回值定位环节:把Action里的返回
View()改成直接返回Content("test"),看请求耗时是否降到100ms以内,如果是可100%定位到视图渲染环节的问题 - 检查DbContext配置:确认返回的模型是否是直接返回的EF实体,查询时是否加了
AsNoTracking(),是否关闭了延迟加载
对应解决方案
- 视图渲染优化:
- 生产环境开启Razor视图预编译,在项目csproj文件里添加配置:
<RazorCompileOnPublish>true</RazorCompileOnPublish> - 不要直接返回EF实体给视图,单独定义扁平化的DTO模型,只保留视图需要的字段,避免反射遍历多余属性或者触发延迟加载
- 生产环境开启Razor视图预编译,在项目csproj文件里添加配置:
- 序列化优化:
- 如果使用System.Text.Json,配置关闭循环引用处理、添加空值忽略规则:
JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,如果是序列化给前端使用建议换成Newtonsoft.Json并加对应的压缩配置
- 如果使用System.Text.Json,配置关闭循环引用处理、添加空值忽略规则:
- Kestrel配置优化:
- Program.cs里调整Kestrel的响应缓冲阈值:
webBuilder.ConfigureKestrel(serverOptions => { serverOptions.Limits.MaxResponseBufferSize = 1024 * 1024; // 设为1MB,大于195KB的业务负载 }); - 开启响应压缩,Program.cs里添加配置:
builder.Services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add<BrotliCompressionProvider>(); options.Providers.Add<GzipCompressionProvider>(); }); // 响应压缩中间件要放在中间件队列的最开头 app.UseResponseCompression();
- Program.cs里调整Kestrel的响应缓冲阈值:
- EF查询优化:
- 所有查询视图用的实体都加
AsNoTracking(),禁用ChangeTracker的跟踪开销 - 全局关闭DbContext的延迟加载,避免视图访问属性时触发隐式数据库查询
- 所有查询视图用的实体都加
内容的提问来源于stack exchange,提问作者Stanley Backlund
相关产品推荐
相关产品推荐

