cpu异常记录

烂柯 发布于 2023-12-22 135 次阅读


一、概述

​ 问题是容器运行的站点有时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)。

image-20231222145303837

image-20231222145352091

四、问题

鹅~ 主要是两部分线程调度,框架内置线程等待wait函数和redis客户端专用线程Pipelines.Sockets.Unofficial.DedicatedThreadPoolPipeScheduler.RunWorkLoop()函数占用较高的。StackExchange.Redis中线程池使用的函数。StackExchange.Redis维护了一个专有线程池(线程数为10)单个SocketManager只持有5个,并发数超过10(每个任务处理的非常慢的极端情况)就会使用.Net 全局线程池。 ,这里专有线程不够使用同样会导致redis的超时。

StackExchange.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

烂柯

最后更新于 2024-04-10