因为手贱引发的性能问题分析
起因
把一个项目从.NET4.5升级到了.NET6,上线后发现项目会偶发性超时,超时发生时间比较集中,但本地的日志监控并未记录到超时日志。
问题定位
先介绍下项目情况:
项目框架:.NET 6.0 WebAPI
部署环境:Windows Server 2016 8核16G
部署方式:IIS
对项目进行压测,发现一个现象,一旦并发达到一定阈值后,接口响应时间就会变慢,从5ms下降到200ms,开始定位问题。
首先怀疑的是项目本身存在性能瓶颈,在接口的输入和输出位置做了监控,但并未发现接口存在因为并发提升而出现性能下降的现象。接口上线前,对处理方法做了性能测试,本地的QPS可以达到2W以上,平均响应处理时长在0.5ms左右,考虑是系统配置问题,所以不做任何操作,只请求简单API接口:
app.MapGet("/home", () => $"OK");
压测后发现性能问题并未改善,基本排除接口内部导致的性能问题,开始转向配置优化,对线程数和ServicePoint的连接限制均作了优化:
ServicePointManager.DefaultConnectionLimit = 512;
ThreadPool.SetMinThreads(256, 256);
性能问题依旧。又考虑到可能是IIS本身的瓶颈,遂改用Windows服务方式部署,性能确实略有提升,但并不理想,响应性能依旧下降,最后,为排除路由转发的性能损耗,直接绕过路由,使用IP直接调用:

接口性能依旧无法提升,同时我发现,无论如何进行压测CPU利用率在达到40%左右都无法进一步提升,已经开始怀疑是.NET自身的保护机制将性能锁死了,问题陷入僵局。

重新整理思路后,决定新建项目重新部署,测试其性能,性能居然大幅度提升,CPU利用率也达到了97%:


开始逐一对项目进行排查,后发现,二者在配置中存在一处不同,新项目中多了关于日志的一项配置:
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
看到这一段,基本上就确定是日志捣的鬼了,因为我用的是自己的日志组件,所以手贱把这段代码删除了,查阅微软的官方文档中有这么一段:

问题解析
按官方文档中所说ASP.NET Core会为框架事件写入日志,但却并未提及默认日志带来的性能问题。

使用sourcedotnet解析WebApplicaitonBuilder我们发现其会通过AddDefaultServicesSlim获取配置默认的日志输出。

因为外部无相关文档,所以我准备手动论证一下此问题,首先将日志的输出级别改为Warning,然后我们通过大量输出Warning日志来看看接口请求是否存在性能下降:
using Microsoft.AspNetCore.Mvc;
namespace EMLogDemo
{
[Route("api/[controller]")]
[ApiController]
public class LoggerController : ControllerBase
{
private readonly ILogger<LoggerController> _logger;
public LoggerController(ILogger<LoggerController> logger)
{
_logger = logger;
}
[HttpGet]
[HttpPost]
public void WirteLog()
{
_logger.LogWarning("这是一个测试");
}
}
}
压力测试后发现性能确实出现了下降:

进一步提升日志的输出数量:
[HttpGet]
[HttpPost]
public void WirteLog()
{
for (int i = 0; i < 10; i++)
{
_logger.LogWarning("这是一个测试");
}
}
性能下降更为明显:

基本验证了.NET默认Logger高并发下确实存在性能问题。
问题猜测
关于.NETLogger高并发为何会出现性能瓶颈,就需要深度解析源码了,在无外部配置是,其默认实现是ConsoleLogger:

一方面,其在日志写入时使用了Queue,并在出入队列时加入了锁来保证线程安全:


那么,问题就来了,既然他默认使用Console进行日志输出,Console是无法并发输出的,问题是否是Console并发瓶颈导致的呢?
[HttpGet]
[HttpPost]
public void WirteLog()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("这是一个测试");
}
}
经过压测发现Console确实会出现高并发时响应时间延长的现象:


我并不清楚是否是此问题导致的,不过我可以问问Deepseek如果.NET6中,如果我删除了appsettings.json配置中的Logging配置会有什么影响
在.NET 6中,如果删除了appsettings.json中的Logging配置,通常不会引发严重问题,因为ASP.NET Core框架在代码中已经设置了默认的日志级别。以下是具体影响和原因:
1. 默认日志级别仍然有效
- ASP.NET Core的
CreateDefaultBuilder方法在代码中预定义了默认日志级别:- Default:
Information - Microsoft.AspNetCore:
Warning
- Default:
- 即使删除
appsettings.json中的Logging配置,这些默认值仍然生效。因此,日志行为不会改变。
2. 环境差异
- 开发环境:默认配置可能包含更详细的日志(如
Developer Exception Page),但日志级别仍由代码默认值控制。 - 生产环境:日志级别默认更保守,与
appsettings.json中的配置无关。
3. 显式代码配置优先
- 如果通过
Program.cs中的ConfigureLogging手动配置了日志级别,这些配置会覆盖appsettings.json。删除文件中的配置不会影响代码中的设置。
4. 潜在影响(如果默认配置被覆盖)
- 如果框架默认配置被修改(例如通过环境变量或其他配置源),删除
appsettings.json可能导致日志级别回退到其他配置源的设置。 - 若无其他配置,日志行为仍由框架默认值保障。
总结
删除appsettings.json中的Logging配置不会导致日志级别失控,因为框架已通过代码确保了合理的默认值(Default: Information, Microsoft.AspNetCore: Warning)。应用会继续按预期记录日志,除非有其他配置覆盖了默认行为。
嗯,他的意思是没问题,你删吧!!!

浙公网安备 33010602011771号