一、概述
问题是容器运行的站点有时CPU资源耗尽,正常情况CPU占用不会操作1%😅😅😅,简单记录排查cpu飙高过程,方便自己后续查阅。官网cpu诊断分析文档
二、环境
- Ubuntu 4核(云服务器,降低成本配置选用很低)
- 容器运行,基础镜像为mcr.microsoft.com/dotnet/aspnet:6.0
- dotnet-counters 检查托管内存的使用情况。
- dotnet-trace 从正在运行的进程中收集诊断跟踪。
#linux系统中工具安装后将工具拷贝至容器中进行使用
#~/.dotnet/tools
dotnet tool install -g dotnet-counters
dotnet tool install -g dotnet-trace
三、监视分析trace
1、监视
#列出可由 dotnet-counters 监视的 dotnet 进程
dotnet-counters ps
#监视的目标进程
dotnet-counters monitor -p <PID>
2、从进程搜集信息
#从进程捕获信息
dotnet-trace collect -p <PID>
#运行大约 35-60 秒,然后按 Enter 退出收集。
#将收集的数据 dotnet_*.nettrace 拷贝下来进行分析
3、分析
这里通过vs打开.nettrace文件,分析时最好结合多个分析工具进行分析(perfview、windbg)。
四、问题
鹅~ 主要是两部分线程调度,框架内置线程等待wait函数和redis客户端专用线程Pipelines.Sockets.Unofficial.DedicatedThreadPoolPipeScheduler.RunWorkLoop()函数占用较高的。StackExchange.Redis中线程池使用的函数。StackExchange.Redis维护了一个专有线程池(线程数为10)单个SocketManager只持有5个,并发数超过10(每个任务处理的非常慢的极端情况)就会使用.Net 全局线程池。 ,这里专有线程不够使用同样会导致redis的超时。
StackExchange.Redis中线程占用描述地址,我反正不是很明白估计是线程被占用又没有及时释放导致(🤔🤔🤔后面空了再重新看下)
五、解决
a、*更改配置,不适用专有线程而使用内置线程
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
ConfigurationOptions config = ConfigurationOptions.Parse(connection);
//使用.net内置线程管理
config.SocketManager = SocketManager.ThreadPool;
services.AddSingleton(ConnectionMultiplexer.Connect(config));
b、*避免Thread Theft
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
c、创建多个ConnectionMultiplexer 实例(根据并发情况设置,也可以简单封装下动态调整),例如
/// <summary>
/// redis连接管理
/// </summary>
public class RedisConnectionManage
{
private static readonly object _lock = new object();
private static int _index = 0;
private static ConnectionMultiplexer[] _connections;
public RedisConnectionManage(string connection, int number)
{
_connections = Enumerable.Range(0, Math.Max(number, 1)).Select(p => ConnectionMultiplexer.Connect(connection)).ToArray();
}
public IConnectionMultiplexer GetConnection()
{
lock (_lock)
{
_index = (_index + 1) % _connections.Length;
return _connections[_index];
}
}
}
public static class RedisExtension
{
public static IServiceCollection AddRedis(this IServiceCollection services, string connection,int number=1)
{
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
services.AddSingleton(new RedisConnectionManage(connection,number));
return services;
}
}
d、设置redis服务端连接数(根据系统设置合适值)
e、redis连接字符串设置过期时间等
`*:6379,configCheckSeconds=60,keepAlive=10
Comments NOTHING