再记一次 应用服务器 CPU 暴高事故分析

一:背景

1. 前言

大概有2个月没写博客了,不是不想写哈????,关注公号的朋友应该知道我这两个月一直都在翻译文章,前前后后大概100篇左右吧,前几天看公号的 常读用户 降了好几十,心疼哈,还得回过神来继续写!

2. 讲故事

上周给 武汉同济 做项目升级,本以为一切顺利,结果捅娄子了,第二天上午高峰期运维说生产上两台 应用服务器 cpu 被打满,影响到所有客户使用,造成了大面积瘫痪,真尬尴,得先让运维抓一个 dump 下来再重启网站,还好,老板人可以,没有问责 ????。

二:CPU 爆高问题分析

1. 找思路

分析 dump,没什么比 windbg 更专业了,不过分析 dump 我还是比较拿手的,那怎么分析呢?最简单粗暴的做法就是看每一个线程当时都在做什么,进而推测一下就 八九不离十 了。

2. 查看所有线程栈

首先用 !t!tp 看一下当前程序的 线程线程池 的整体概况。


0:000> !t
ThreadCount:      60
UnstartedThread:  0
BackgroundThread: 38
PendingThread:    0
DeadThread:       22
Hosted Runtime:   noLock  ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception11    1 2c24 02487038     28220 Preemptive  00000000:00000000 010df4f8 0     Ukn 28    2 2ca0 024bad90     2b220 Preemptive  00000000:00000000 010df4f8 0     MTA (Finalizer) 30    3 2d04 024f1450   102a220 Preemptive  00000000:00000000 010df4f8 0     MTA (Threadpool Worker) 31    4 2054 024fb188     21220 Preemptive  00000000:00000000 010df4f8 0     Ukn 32    6 1128 02574400   1020220 Preemptive  00000000:00000000 010df4f8 0     Ukn (Threadpool Worker) 2    5 27ac 02520da8     20220 Preemptive  00000000:00000000 010df4f8 0     Ukn 35   17 2c44 1cc362c8   202b220 Preemptive  00000000:00000000 024fa838 1     MTA 36   20 1740 1cccc748     21220 Preemptive  00000000:00000000 010df4f8 0     Ukn 37   21 16c4 1cc08240     21220 Preemptive  00000000:00000000 010df4f8 0     Ukn 38   22 16a8 1ccd28b8     21220 Preemptive  00000000:00000000 010df4f8 0     Ukn ....0:000> !tp
CPU utilization: 97%
Worker Thread: Total: 21 Running: 21 Idle: 0 MaxLimit: 8191 MinLimit: 8
Work Request in Queue: 23Unknown Function: 6d92a17f  Context: 0109b5f0Unknown Function: 6d92a17f  Context: 0107ed90Unknown Function: 6d92a17f  Context: 0104e750Unknown Function: 6d92a17f  Context: 010a0200AsyncTimerCallbackCompletion TimerInfo@207f8008AsyncTimerCallbackCompletion TimerInfo@0251b418Unknown Function: 6d92a17f  Context: 01096c78Unknown Function: 6d92a17f  Context: 01081398AsyncTimerCallbackCompletion TimerInfo@024d0120Unknown Function: 6d92a17f  Context: 010a9a20Unknown Function: 6d92a17f  Context: 01057950Unknown Function: 6d92a17f  Context: 0104c2d0Unknown Function: 6d92a17f  Context: 010943d8Unknown Function: 6d92a17f  Context: 0107a180Unknown Function: 6d92a17f  Context: 010a7418Unknown Function: 6d92a17f  Context: 010839a0Unknown Function: 6d92a17f  Context: 010678d0Unknown Function: 6d92a17f  Context: 010a2808Unknown Function: 6d92a17f  Context: 0105c250Unknown Function: 6d92a17f  Context: 0108abb8Unknown Function: 6d92a17f  Context: 0108f7c8Unknown Function: 6d92a17f  Context: 0108d1c0Unknown Function: 6d92a17f  Context: 20896498
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 1 Free: 1 MaxFree: 16 CurrentLimit: 1 MaxLimit: 1000 MinLimit: 8

从上面的输出大概可以看到如下几点信息:

  • 当前程序有 60 个线程。

  • 当前 CPU 利用率为 97%,爆高无疑。

  • 线程池中有21个线程全部打满,还有23个任务在 任务队列 中排队等待处理。

总的来看,系统已经高负荷,不堪重负了,接下来的一个疑问就来了,所有的线程都被打满而且线程池中还有大量等待处理的任务在排队,现有的线程都在干嘛呢?难道不处理吗?

2. 查看所有线程的托管线程栈

既然现存的有 60 个线程,那我就使用 ~ *e !clrstack 命令看看所有的线程都在干嘛?


0:000> ~ *e !clrstack
OS Thread Id: 0x8d8 (44)
Child SP       IP Call Site
1ad8d750 7759f901 [InlinedCallFrame: 1ad8d750] 
1ad8d74c 71e1a9ea DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr, IntPtr, System.String, System.String, Int32, Int32, System.String, Int32, Int32, Int32)
1ad8d750 71d52f0b [InlinedCallFrame: 1ad8d750] System.Globalization.CompareInfo.InternalCompareString(IntPtr, IntPtr, System.String, System.String, Int32, Int32, System.String, Int32, Int32, Int32)
1ad8d7b4 71d52f0b System.Globalization.CompareInfo.Compare(System.String, Int32, Int32, System.String, Int32, Int32, System.Globalization.CompareOptions)
1ad8d7e0 71e16ab9 System.String.Compare(System.String, Int32, System.String, Int32, Int32, System.Globalization.CultureInfo, System.Globalization.CompareOptions)
1ad8d810 71d51c8e System.Globalization.DateTimeFormatInfo.Tokenize(System.TokenType, System.TokenType ByRef, Int32 ByRef, System.__DTString ByRef)
1ad8d86c 71d51a92 System.__DTString.GetSeparatorToken(System.Globalization.DateTimeFormatInfo, Int32 ByRef, Char ByRef)
1ad8d88c 71d513c4 System.DateTimeParse.Lex(DS, System.__DTString ByRef, System.DateTimeToken ByRef, System.DateTimeRawInfo ByRef, System.DateTimeResult ByRef, System.Globalization.DateTimeFormatInfo ByRef, System.Globalization.DateTimeStyles)
1ad8d8dc 71d50b59 System.DateTimeParse.TryParse(System.String, System.Globalization.DateTimeFormatInfo, System.Globalization.DateTimeStyles, System.DateTimeResult ByRef)
1ad8d974 71dfce8b System.DateTimeParse.Parse(System.String, System.Globalization.DateTimeFormatInfo, System.Globalization.DateTimeStyles)
1ad8d9d8 7243c9bc System.Convert.ToDateTime(System.String, System.IFormatProvider)
1ad8d9f8 724369b1 System.String.System.IConvertible.ToDateTime(System.IFormatProvider)
1ad8da00 7243c8a2 System.Convert.ToDateTime(System.Object)

由于输出的太多,这里就简略输出了,不过我大体罗列了一下线程大概都在做什么事情。

  • 有 9 个线程正在执行 GetAllByCondition() 方法

  • 有 4 个线程正在执行 GetDayInvoice() 方法

9 + 4 个线程都在搞这两个方法,这就比较可疑了,不过从各个线程的栈顶上看并没有类似 wait 关键词,这就意味着大家不是在争抢锁啥的,那问题在哪里呢?

继续分析这两个方法到底在数据库中读了什么?通过 !dso 抓取 GetDayInvoice() 方法中的 sql,这里我就模糊一下了哈,windbg命令大概如下:


0:000> ~45s
eax=1c06a5c8 ebx=00000000 ecx=59002090 edx=59002090 esi=000003d4 edi=00000000
eip=7759f901 esp=1d95cfa8 ebp=1d95d014 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!NtWaitForSingleObject+0x15:
7759f901 83c404          add     esp,4
0:045> !dso
OS Thread Id: 0x2a04 (45)
ESP/REG  Object   Name
1D95D6D0 aaaac9f4 System.String    SELECT xxxxxx FROM xxxx as v WITH(NOLOCK) left join xxx as cr WITH(NOLOCK) on v.xxx=cr.xxx left join xxx  as crr WITH(NOLOCK) on cr.PID=crr.ID  WHERE xxx IN (SELECT DISTINCT xxx FROM xxxx WITH(NOLOCK) WHERE (SendTime>='2021-01-21 14:30:39' OR  xxx>='2021-01-21 14:30:39' OR xxx>='2021-01-21 14:30:39')  AND ((InvoiceType  =1 and( IsRepeat=0 or IsRepeat is null ))  OR xxx IN(16,15))  )  

然后让运维查了下,这条sql大概有 13w 的记录,我第一反应就是查这么大的数据是不是有毛病撒,问了下懂业务的老大,这一块原来是做 初始化同步, 而且这块好久都没人动过,言外之意原来也是这么查的,一点毛病也没有呀,我去,说的也是哈,为啥以前没问题呢???

3. 查看托管堆

既然一条sql查了 13w 条数据,刚才是 4个线程在执行 GetDayInvoice(),也就意味着有 52w 条数据正在从 sqlserver 中获取,接下来我的本能反应就是看看托管堆,使用 !dumpheap -stat 就可以了,如下代码所示:


0:045> !dumpheap -stat
The garbage collector data structures are not in a valid state for traversal.
It is either in the "plan phase," where objects are being moved around, or
we are at the initialization or shutdown of the gc heap. Commands related to 
displaying, finding or traversing objects as well as gc heap segments may not 
work properly. !dumpheap and !verifyheap may incorrectly complain of heap 
consistency errors.
Object <exec cmd="!ListNearObj /d 02881000">02881000</exec> has an invalid method table.

我去,有点尴尬,居然报错了,先擦擦头上的汗,这句话:The garbage collector data structures are not in a valid state for traversal 引起了我的警觉,当前托管堆是无效状态,gc被触发了。。。当前还是 plan phase 状态,是不是 gc 导致了 cpu 爆高呢?

4. 零碎信息整合

通过上面这些零碎的信息,不知道大家可整明白了,让我来说的话,简而言之:GetDayInvoice() 读了大量数据,导致gc频繁回收,进而导致 cpu 爆高,这里有几个概念需要大家了解下:

  • 这个程序是 32bit,意味着最大只能吃 4G 内存。

  • 32bit 的临时代( 0+1 代) 大概 几十M 的空间。

  • IIS 是 服务器模式 的GC,意味着当前的托管线程会临时充当 GC 回收线程。

尤其是上面第三个概念,既然当前gc被触发了,也就意味着托管线程被临时给 GC 征用了,那我可以看下是否真的是这样,可以用 ~ *e !dumpstack 查看所有线程的托管和非托管的所有栈信息,如下图所示:

可以清晰的看到,调用 GetDayInvoice() 的线程在 CLR 层面触发了 gc,而此时 gc 想调用 try_allocate_more_space 来给自己分配更多的空间,而且 wait_for_gc_done 也表示它在等待其他gc线程处理完成,所以就卡死在这里了。

如果不信的话,还可以继续观察其他线程的非托管堆栈,如下图所示:

clr!SVR::GCHeap::WaitUntilGCComplete+0x35, calling clr!CLREventBase::WaitEx 可以看出,都是在等待 GC 完成,导致 CPU 爆高。

5. 找到问题根源

汇总一下就是:这次 cpu 爆高是因为 32bit 程序只能吃 4G 内存,而程序需要同步大量的数据,导致内存被打满,gc无法分配更多的内存让程序使用,gc的机制让 cpu 打的满满的,知道问题之后,解决办法很简单,将 iis 的 应用程序域 的配置 启用32bit应用程序 改成 False 即可,这样程序就可以以 64bit 方式运行,如下图所示:

三:总结

很显然这次事件是因为管理混乱造成的,因为历史遗留问题,有些网站必须用 32 bit 运行,有些必须用 64 bit 运行,据运维说,目前服务器存在不能建过多的应用程序域,导致多个网站公用一个程序域,表面上是运维弄错应用程序域,根子上还是没有彻底改造的决心!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/304963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

”残酷“人生第一步

仔仔终于要面对人生中的第一次巨大困难&#xff1a;断奶了。恰逢夫人要出差几日&#xff0c;刚刚开始&#xff0c;我们都很犹豫。确实很怕他整夜哭闹。但是思考再三还是决定&#xff0c;让小家伙独自面对吧。于是&#xff0c;他的“残酷”人生第一步&#xff0c;就这样准备开始…

IdentityServer4之Authorization Code(授权码)相对更安全

前言接着授权模式聊&#xff0c;这次说说Authorization Code(授权码)模式&#xff0c;熟悉的微博接入、微信接入、QQ接入都是这种方式(这里说的是oauth2.0的授权码模式)&#xff0c;从用户体验上来看&#xff0c;交互方式和Implicit没啥改变&#xff0c;随便找个网站瞅瞅&#…

后端根据百度地图真实路径距离_导航软件哪家强?实测百度地图and高德地图哪个更靠谱...

随着社会的不断发展&#xff0c;人们的生活越来越离不开地图导航&#xff0c;无论是开车出行还是到去到陌生的地方&#xff0c;我们都会用到手机地图。然而在众多导航软件中&#xff0c;使用最为广泛的就要属百度地图和高德地图了。但别看都是导航软件&#xff0c;其中差别还真…

一文读懂 KMP 算法

字符串匹配是计算机的基本任务之一。举例来说&#xff0c;有一个字符串"BBC ABCDAB ABCDABCDABDE"&#xff0c;我想知道&#xff0c;里面是否包含另一个字符串"ABCDABD"&#xff1f; 许多算法可以完成这个任务&#xff0c;Knuth-Morris-Pratt算法&#xff…

128位加密SSL证书

SGC超真SSL(SGC ZhenSSL)属于 SGC Enabled High Assurance SSL&#xff0c; 是 WoSign 的增强型 SSL证书产品&#xff0c;支持 SGC 强制128位加密技术&#xff0c;即使用户的浏览器只支持 40 位( 如 IE4.X) 或 56 位 ( 如 IE5.X) 也能自动强制实现至少 128 位的高强度加密&…

eclipse连接mysql_专题一、flask构建mysql数据库正确姿势

每周壹总结&#xff0c;一起共同充电第121篇应用程序最核心的就是数据&#xff0c;每天我们写程序其实也是在处理数据的过程&#xff0c;那么很有必要系统性的讲讲和梳理python的flask框架是如何进行数据交互操作的。趁这3天假期&#xff0c;分4篇内容来系统的讲讲&#xff0c;…

C#多线程和异步(二)——Task和async/await详解

一、什么是异步同步和异步主要用于修饰方法。当一个方法被调用时&#xff0c;调用者需要等待该方法执行完毕并返回才能继续执行&#xff0c;我们称这个方法是同步方法&#xff1b;当一个方法被调用时立即返回&#xff0c;并获取一个线程执行该方法内部的业务&#xff0c;调用者…

从头到尾彻底理解傅里叶变换算法(上)

从头到尾彻底理解傅里叶变换算法&#xff08;上&#xff09; 前言 第一部分、 DFT 第一章、傅立叶变换的由来 第二章、实数形式离散傅立叶变换&#xff08;Real DFT&#xff09; 从头到尾彻底理解傅里叶变换算法、下 第三章、复数 第四章、复数形式离散傅立叶变换 前言&#x…

使用ADO.NET的参数集合来有效防止SQL注入漏洞

SQL注入漏洞是个老话题了&#xff0c;在以前做ASP做开发时&#xff0c;就经常需要用字符串的过虑等方式来解决这个问题&#xff0c;但有时候确做的不够彻底&#xff0c;往往让***钻了空子。那么目前在我们.NET中&#xff0c;不管是用WINFORM开发还是用WEBFORM&#xff0c;连接数…

[Abp 源码分析]ASP.NET Core 集成

点击上方蓝字关注我们0. 简介整个 Abp 框架最为核心的除了 Abp 库之外&#xff0c;其次就是 Abp.AspNetCore 库了。虽然 Abp 本身是可以用于控制台程序的&#xff0c;不过那样的话 Abp 就基本没什么用&#xff0c;还是需要集合 ASP.NET Core 才能发挥它真正的作用。在 Abp.AspN…

从头到尾彻底理解傅里叶变换算法(下)

从头到尾彻底理解傅里叶变换算法&#xff08;上&#xff09;&#xff0c;请看今天第一条。 以下继续&#xff1a; 第三章、复数 复数扩展了我们一般所能理解的数的概念&#xff0c;复数包含了实数和虚数两部分&#xff0c;利用复数的形式可以把由两个变量表示的表达式变成由一个…

树莓派安装python3.5_梦见树_周公解梦梦到树是什么意思_做梦梦见树好不好_周公解梦官网...

梦见树是什么意思&#xff1f;做梦梦见树好不好&#xff1f;梦见树有现实的影响和反应&#xff0c;也有梦者的主观想象&#xff0c;请看下面由(周公解梦官网www.zgjm.org)小编帮你整理的梦见树的详细解说吧。树主健康&#xff0c;树笔直挺拔&#xff0c;象征着人的健康。 在梦中…

[Abp 源码分析]后台作业与后台工作者

点击上方蓝字关注我们0. 简介在某些时候我们可能会需要执行后台任务&#xff0c;或者是执行一些周期性的任务。比如说可能每隔 1 个小时要清除某个临时文件夹内的数据&#xff0c;可能用户会要针对某一个用户群来群发一组短信。前面这些就是典型的应用场景&#xff0c;在 Abp 框…

【转】x.509证书在WCF中的应用(CS篇)

【转自】x.509证书在WCF中的应用(CS篇) 为什么要用x.509证书? WCF的服务端和客户端之间&#xff0c;如 果不作任何安全处理(即服务端的<security mode"None">)&#xff0c;则所有传输的消息将以明文方式满天飞&#xff0c;在internet/intranet环境下无疑是很不…

从概念到案例:初学者须知的十大机器学习算法

本文先为初学者介绍了必知的十大机器学习&#xff08;ML&#xff09;算法&#xff0c;并且我们通过一些图解和实例生动地解释这些基本机器学习的概念。我们希望本文能为理解机器学习基本算法提供简单易读的入门概念。 机器学习模型 在《哈佛商业评论》发表「数据科学家是 21 世…

测试Live Write的插件

1、文字竖排&#xff1a; 删除&#xff0c;只因首页显示时太占空间。2、酷表情&#xff1a;3、Rhapsody SongI am listening to Sad Songs And Waltzes by Cake . Rhapsody.

手把手教你用7行代码实现微信聊天机器人 -- Python wxpy

环境要求&#xff1a; Windows / Linux / Mac OS Python 3.4-3.6&#xff0c;以及 2.7 版本 wxpy安装 ## 使用国内源安装速度快 pip install -U wxpy -i "https://pypi.doubanio.com/simple/" 实例 让机器人与所有好友聊天 from wxpy import * # 实例化&#xff0c;并…

Dapr 已在塔架就位 将发射新一代微服务

微服务是云原生架构的核心&#xff0c;通常使用Kubernetes 来按需管理服务扩展。微软一直走在 Cloud Native Computing Foundation的 最前沿&#xff0c;并通过使用Kubernetes来支持其超大规模Azure和其混合云Azure Stack&#xff0c;微软对云原生的投资一部分来自其工具&#…

python 复制文件_10 行 Python 代码写 1 个 USB 病毒

(给Python开发者加星标&#xff0c;提升Python技能)转自&#xff1a; 知乎-DeepWeaver昨天在上厕所的时候突发奇想&#xff0c;当你把usb插进去的时候&#xff0c;能不能自动执行usb上的程序。查了一下&#xff0c;发现只有windows上可以&#xff0c;具体的大家也可以搜索(搜索…

html5中外描边怎么写,CSS3实现文字描边的2种方法(小结)

问题最近遇到一个需求&#xff0c;需要实现文字的描边效果&#xff0c;如下图解决方法一首先想到去看CSS3有没有什么属性可以实现&#xff0c;后来被我找到了text-stroke该属性是一个复合属性&#xff0c;可以设置文字宽度和文字描边颜色该属性使用很简单&#xff1a;text-stro…