zoukankan      html  css  js  c++  java
  • 记一次某网站生产环境CPU忽高忽低故障解决过程

    感谢

    感谢【一级码农】 的帮助,之前也读了大佬的好多文章,一直在学习中,也没有实际操作过。
    这次的过程也是在大佬的指点下完成的。

    现象描述

    从周六上午开始,陆续收到服务器CPU高的报警短信,到下午已经累计三十多条报警了,看来确实得分析一下原因了。
    打开云监控,与实际情况一致,CPU居高不下
    image.png

    通过进程,很快锁定是哪个应用,是一个car系统造成的
    image.png
    car系统的版本是.net 4.0
    image.png

    分析过程

    通过以下命令,抓包,命令解释,当CPU超过50%时,持续三秒,抓两个包。进程ID是6100。进程ID在任务管理器中可以看到。

    procdump -ma -c 50 -s 3 -n 2 6100
    

    遇到如下错误
    image.png
    出现以上错误是因为没有用管理员运行CMD,重新用管理员打开即可。
    image.png
    很快抓包完成。从服务器down到本地。

    # 加载sos和clr文件
    0:016> .load C:WindowsMicrosoft.NETFrameworkv4.0.30319sos.dll
    0:016> .load C:WindowsMicrosoft.NETFrameworkv4.0.30319clr.dll
    
    0:062> !runaway
     User Mode Time
      Thread       Time
      16:dc4       0 days 0:13:12.593
      15:2be4      0 days 0:13:06.390
      17:2404      0 days 0:13:06.328
      18:bb4       0 days 0:12:45.953
      39:3294      0 days 0:04:10.500
      44:320       0 days 0:04:07.281
      38:3b24      0 days 0:04:06.156
      43:176c      0 days 0:04:00.218
      46:1118      0 days 0:03:55.359
      50:2a3c      0 days 0:03:54.921
      40:1aac      0 days 0:03:50.531
      51:2fb4      0 days 0:03:49.421
      47:2508      0 days 0:03:48.718
    
    

    !runaway发现前几个线程时间很长,那么他们在干什么呢?
    切到 16 号线程~16s

    0:016> !clrstack
    PDB symbol for clr.dll not loaded
    Failed to load data access DLL, 0x80004005
    Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
                2) the file mscordacwks.dll that matches your version of clr.dll is 
                    in the version directory or on the symbol path
                3) or, if you are debugging a dump file, verify that the file 
                    mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
                4) you are debugging on supported cross platform architecture as 
                    the dump file. For example, an ARM dump file must be debugged
                    on an X86 or an ARM machine; an AMD64 dump file must be
                    debugged on an AMD64 machine.
    
    You can also run the debugger command .cordll to control the debugger's
    load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
    If that succeeds, the SOS command should work on retry.
    
    If you are debugging a minidump, you need to make sure that your executable
    path is pointing to clr.dll as well.
    

    上面不是提示sos clr文件不对吗,在服务器上分析是没有报这个错的,所以我从服务器上弄了两个回来

    .load D:softWinDbg+procdumpprocdumpcarsos.dll
    .load D:softWinDbg+procdumpprocdumpcarclr.dll

    0:018> .load D:softWinDbg+procdumpprocdumpcarsos.dll
    0:018> .load D:softWinDbg+procdumpprocdumpcarclr.dll
    0:018> !clrstack
    PDB symbol for clr.dll not loaded
    OS Thread Id: 0xbb4 (18)
    Unable to walk the managed stack. The current thread is likely not a 
    managed thread. You can run !threads to get a list of managed threads in
    the process
    Failed to start stack walk: 80070057
    
    

    OK,现在不提示配置问题了。继续分析

    0:016> !clrstack
    OS Thread Id: 0xdc4 (16)
    Unable to walk the managed stack. The current thread is likely not a 
    managed thread. You can run !threads to get a list of managed threads in
    the process
    Failed to start stack walk: 80070057
    0:016> ~15s
    eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=0000053c
    eip=76f2c0bc esp=1af3fbfc ebp=1af3fc6c iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!NtWaitForSingleObject+0xc:
    76f2c0bc c20c00          ret     0Ch
    
    

    前四个线程,都提示not a managed thread
    image.png
    请教了一下【一级码农】大佬,这个方向可能是错误的。那我们就只能换一个方向。
    这里大佬已经告诉我是拖管线程的问题,如果是我们首次分析,可能还需要先判断是拖管还是非拖管的问题。这个后面再慢慢消化加上去。
    我们这里走一个捷径,直接查拖管线程,有时候你也可以假装碰碰运气,说不定就能找到原因了呢。

    ~*e !clrstack 发现触发了GC回收
    image.png

    image.png

    !dumpstack 显示当前调用栈上的所有托管对象的信息,
    image.png

    发现这个Volcker.Autotrader.ProductPropertieData.GetAll()方法出现多次,明显有多个线程卡在这里了。那这个方法是干啥的呢,
    查看一下源码就知道了。
    当然,如果有时候你手里没有源码,或者服务器运行的和本地代码差异比较大,那就导出源代码

    0:053> !ip2md 1c15f4bd
    MethodDesc:   1b9ec174
    Method Name:  Volcker.Autotrader.ProductPropertieData.GetAll()
    Class:        1bd668a4
    MethodTable:  1b9ec19c
    mdToken:      06000088
    Module:       1b9ebbd4
    IsJitted:     yes
    CodeAddr:     1c15f420
    Transparency: Critical
    0:053> !savemodule 1b9ebbd4 D:dumpscar-cpuGetAll.dll
    3 sections in file
    section 0 - VA=2000, VASize=1714c, FileAddr=200, FileSize=17200
    section 1 - VA=1a000, VASize=3e8, FileAddr=17400, FileSize=400
    section 2 - VA=1c000, VASize=c, FileAddr=17800, FileSize=200
    

    需要提前创建好目录 D:dumpscar-cpu,找到上面的方法
    image.png
    这一行代码是干啥的呢,解释一下,其实是个通用的将数据库表转换为实体类的方法。

            /// <summary>
            /// 根据IDataReader对属性进行赋值
            /// </summary>
            /// <param name="reader">
            new public void LoadDataReader(IDataReader reader)
            {
                LoadDataReader(MethodBase.GetCurrentMethod(), this, reader);
            }
            
            /// <summary>
            /// 根据DataReader为对象属性赋值
            /// </summary>
            /// <param name="method">所属类MethodBase对象
            /// <param name="obj">
            /// <param name="reader">设置其属性 (Property) 值的对象
            /// <returns></returns>
            virtual public void LoadDataReader(MethodBase method, Object obj,IDataReader reader)
            {
                Hashtable cols = new Hashtable();
                for (var i = 0; i < reader.FieldCount; i++)
                {
                    cols.Add(reader.GetName(i).ToLower(), reader.GetValue(i));	//装入数据到hashtable中
                }
                Hashtable h = GetFieldProperties(method, FieldType.DBField);	//获取属性的Hasttable
                foreach (var o in h.Keys)
                {
                    PropertyInfo p = (PropertyInfo)h[o];
                    Object v = null;
    
                    if (cols.Contains(o))
                    {
                        v = cols[o];
                    }               
    
                    if (v != null)
                    {
                        SetPropertieValue(ref obj, ref p, ref v);
                    }
                }
            }
    

    这里用了一个反射,通过上面的分析得到,问题出在这一行
    Hashtable h = GetFieldProperties(method, FieldType.DBField); //获取属性的Hasttable
    可能是这个类或者方法的属性太多吧,或者其他原因造成的。
    可能一开始写这个反射方法用法没有错,但是防不住后面有人在类中或者方法中加了其他的东东,造成反射有问题了。
    我以前也写过类似的方法,没有这个智能,但也从来没出过问题。
    有时候,太智能了也没有啥必要,
    最简单的代码效率最高,搞这么复杂有必要吗?
    不说这个了,解决问题先
    由于代码历史太悠久,相关细节完全不清楚,所以也不敢改动太大
    目前的办法就是加缓存,这个其实是个字典表,理论上变化不会很大,所以加了24小时的缓存,24小时请求一次。
    改一下代码,更新一下看看。

    效果很明显啊,再观察几天看看。
    image.png

    总结

    上面的分析过程基本上比较详细了,但有些步骤可能没有啥逻辑,原因就是靠经验,或者运气。经历的越多,可能猜一下就大概知道原因在哪
    再次感谢【一线码农】的帮助,下面是大佬的总结
    image.png

    参考文章

    一线码农的github
    教你配置windows上的windbg,linux上的lldb,打入clr内部这一篇就够了

    作者:朱鹏飞 加我个人微信(zhupengfeivip)进入郑州IT微信交流群
    个人博客:https://www.cnblogs.com/zhupengfei/
    image.png
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    vue点击实现箭头的向上与向下
    ES6中箭头函数加不加大括号的区别
    angular图片的两种表达方式
    通过添加类名实现样式的变化
    angular中路由跳转并传值四种方式
    Sublime Text 3 设置文件详解(settings文件)
    Second:eclipse配置Tomcat
    First:安装配置JDK and 部署Tomcat
    本地环境代码一码云一服务器代码部署
    2.sublime设置本地远程代码同步
  • 原文地址:https://www.cnblogs.com/zhupengfei/p/15338702.html
Copyright © 2011-2022 走看看