记一次 .NET 某电商交易平台Web站 CPU爆高分析
一:背景
1. 讲故事
已经连续写了几篇关于内存暴涨的真实案例,有点麻木了,这篇换个口味,分享一个 CPU爆高
的案例,前段时间有位朋友在 wx 上找到我,说他的一个老项目经常收到 CPU > 90%
的告警信息,挺尴尬的。
既然找到我,那就用 windbg 分析呗,还能怎么办。
二:windbg 分析
1. 勘探现场
既然说 CPU > 90%
,那我就来验证一下是否真的如此?
0:359> !tp
CPU utilization: 100%
Worker Thread: Total: 514 Running: 514 Idle: 0 MaxLimit: 2400 MinLimit: 32
Work Request in Queue: 1Unknown Function: 00007ff874d623fc Context: 0000003261e06e40
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 2 Free: 2 MaxFree: 48 CurrentLimit: 2 MaxLimit: 2400 MinLimit: 32
从卦象看,真壮观,CPU直接被打满,线程池里 514 个线程也正在满负荷奔跑,那到底都奔跑个啥呢?首先我得怀疑一下这些线程是不是被什么锁给定住了。
2. 查看同步块表
观察锁情况,优先查看同步块表,毕竟大家都喜欢用 lock 玩多线程同步,可以用 !syncblk
命令查看。
0:359> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner53 000000324cafdf68 498 0 0000000000000000 none 0000002e1a2949b0 System.Object
-----------------------------
Total 1025
CCW 3
RCW 4
ComClassFactory 0
Free 620
我去,这卦看起来很奇怪, MonitorHeld=498
是什么鬼???教科书上都说: owner + 1 , waiter + 2
,所以你肉眼看到的总会是一个奇数,那偶数又是个啥意思?查了下神奇的 StackOverflow,大概总结成如下两种情况:
内存损坏
这种情况比中彩还难,我也坚信不会走这种天罗运。。。
lock convoy (锁护送)
前段时间我分享了一篇真实案例:记一次 .NET 某旅行社Web站 CPU爆高分析 ,它就是因为 lock convoy 造成的 CPU 爆高,果然世界真小,又遇到了。。。为了方便大家理解,我还是把那张图贴上吧。
看完这张图你应该就明白了,一个线程在时间片内频繁的争抢锁和上下文切换,所以就很容易的出现一个持有锁的线程刚退出,那些等待锁的线程此时还没有一个真正的持有锁,刚好抓到的dump就是这么一个时间差,换句话说,当前的 498 全部是 waiter 线程的计数,也就是 249 个 waiter 线程,接下来就可以去验证了,把所有线程的线程栈调出来,再检索下 Monitor.Enter
关键词。
从图中可以看出当前有 220 个线程正卡在 Monitor.Enter
处,貌似丢了29个,不管了,反正大量线程卡住就对了,从堆栈上看貌似是在 xxx.Global.PreProcess
方法中设置上下文后卡住了,为了满足好奇心,我就把问题代码给导出来。
3. 查看问题代码
还是用老命令 !ip2md + !savemodule
。
0:359> !ip2md 00007ff81ae98854
MethodDesc: 00007ff819649fa0
Method Name: xxx.Global.PreProcess(xxx.JsonRequest, System.Object)
Class: 00007ff81966bdf8
MethodTable: 00007ff81964a078
mdToken: 0000000006000051
Module: 00007ff819649768
IsJitted: yes
CodeAddr: 00007ff81ae98430
Transparency: Critical
0:359> !savemodule 00007ff819649768 E:\dumps\PreProcess.dll
3 ps in file
p 0 - VA=2000, VASize=b6dc, FileAddr=200, FileSize=b800
p 1 - VA=e000, VASize=3d0, FileAddr=ba00, FileSize=400
p 2 - VA=10000, VASize=c, FileAddr=be00, FileSize=200
然后用 ILSpy 打开问题代码,截图如下:
尼玛,果然每个 DataContext.SetContextItem()
方法中都有一个 lock 锁,完美命中 lock convoy
。
4. 真的就这样结束了吗?
本来准备汇报了,但想着500多个线程栈都调出来了,闲着也是闲着,干脆扫扫看吧,结果我去,意外发现有 134 个线程卡在 ReaderWriterLockSlim.TryEnterReadLockCore
处,如下图所示:
从名字上可以看出,这是一个优化版的读写锁:ReaderWriterLockSlim
, 真的很好奇,再次导出问题。
internal class LocalMemoryCache : ICache
{private string CACHE_LOCKER_PREFIX = "xx_xx_";private static readonly NamedReaderWriterLocker _namedRwlocker = new NamedReaderWriterLocker();public T GetWithCache<T>(string cacheKey, Func<T> getter, int cacheTimeSecond, bool absoluteExpiration = true) where T : class{T val = null;ReaderWriterLockSlim @lock = _namedRwlocker.GetLock(cacheKey);try{@lock.EnterReadLock();val = (MemoryCache.Default.Get(cacheKey) as T);if (val != null){return val;}}finally{@lock.ExitReadLock();}try{@lock.EnterWriteLock();val = (MemoryCache.Default.Get(cacheKey) as T);if (val != null){return val;}val = getter();CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();if (absoluteExpiration){cacheItemPolicy.AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(cacheTimeSecond));}else{cacheItemPolicy.SlidingExpiration = TimeSpan.FromSeconds(cacheTimeSecond);}if (val != null){MemoryCache.Default.Set(cacheKey, val, cacheItemPolicy);}return val;}finally{@lock.ExitWriteLock();}}
看了下上面的代码大概想实现一个对 MemoryCache 的 GetOrAdd 操作,而且貌似为了安全起见,每一个 cachekey 都配了一把 ReaderWriterLockSlim,这逻辑就有点奇葩了,毕竟 MemoryCache 本身就带了实现此逻辑的线程安全方法,比如:
public class MemoryCache : ObjectCache, IEnumerable, IDisposable
{public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null){if (regionName != null){throw new NotSupportedException(R.RegionName_not_supported);}CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();cacheItemPolicy.AbsoluteExpiration = absoluteExpiration;return AddOrGetExistingInternal(key, value, cacheItemPolicy);}
}
5. 用 ReaderWriterLockSlim 有什么问题吗?
哈哈,肯定有很多朋友这么问?????????????,确实,这有什么问题呢?首先看一下 _namedRwlocker 集合中目前到底有多少个 ReaderWriterLockSlim ? 想验证很简单,上托管堆搜一下即可。
0:359> !dumpheap -type System.Threading.ReaderWriterLockSlim -stat
Statistics:MT Count TotalSize Class Name
00007ff8741631e8 70234 6742464 System.Threading.ReaderWriterLockSlim
可以看到当前托管堆有 7w+ 的 ReaderWriterLockSlim,这又能怎么样呢???不要忘啦, ReaderWriterLockSlim 之所以带一个 Slim
,是因为它可以实现一段时间内的用户态 自旋
,那 自旋
就得吃一点CPU,如果再放大几百倍?CPU能不被抬起来吗?
三:总结
总的来说,这个 Dump 所反应出来的 CPU打满
有两个原因。
lock convoy 造成的频繁争抢和上下文切换给了 CPU 一顿暴击。
ReaderWriterLockSlim 的百倍
用户态自旋
又给了 CPU 一顿暴击。
知道原因后,应对方案也就简单了。
批量操作,降低串行化的 lock 个数,不要玩锁内卷。
去掉 ReaderWriterLockSlim,使用 MemoryCache 自带的线程安全方法。
记一次 .NET 某电商交易平台Web站 CPU爆高分析相关推荐
- .NET 某电商交易平台Web站 CPU爆高分析
勘探现场 既然说 CPU > 90%,那我就来验证一下是否真的如此? 0:359> !syncblk Index SyncBlock MonitorHeld Recursion Ownin ...
- 构建船舶航海用品B2B电商交易平台,健壮产业生态
电子商务正以一种巨大的力量影响着传统企业,海运业也不例外.根据ShipParts.com大数据显示在过去的一年中,来自国外的订单及售后配件需求呈现出爆发式的增长,电子商务正以不可阻挡的势头改变着行业生 ...
- 买卖汽车电商交易平台、选车社区论坛、买新车、二手车交易平台、移动端汽车电商平台、web端汽车运营管理平台、供应商管理、营销商管理、新车交易管理、调车管理、汽车顾问、车库管理、出入库管理、Axure原型
买卖汽车电商交易平台.选车社区论坛.买新车.二手车交易平台.移动端汽车电商平台.web端汽车运营管理平台.供应商管理.营销商管理.新车交易管理.调车管理.汽车顾问.车库管理.出入库管理.Axure原型 ...
- 跨境电商自建站后台系统原型rp_Shoptago---跨境电商平台又一个新选择
在去年邀请2000卖家参与内测的跨境电商自建站平台Shoptago,现在用户数已经达到了3850+,成绩可谓,我们也能在各大媒体看到Shoptago相关的新功能上线公告,那么在这段时间里,Shopta ...
- web电商系统、电商平台WEB端交互原型模板、用户中心、会员中心、优惠券、积分、互动社区、运营推广、内容推荐、商品展示、订单流程、订单管理、售后及服务、Axure原型、rp原型、电商原型、商城系统原型
作品介绍:web电商系统.电商平台WEB端交互原型模板.用户中心.会员中心.优惠券.积分.互动社区.运营推广.内容推荐.商品展示.订单流程.订单管理.售后及服务等完整的电商体系功能架构和业务流程 Ax ...
- 利用python分析电商_基于Word2Vec+SVM对电商的评论数据进行情感分析
Word2Vec-sentiment 基于Word2Vec+SVM对电商的评论数据进行情感分析 首先是利用word2vec对正负评论数据进行词向量训练,然后利用SVM分类器对语料进行分类,具体的过程如 ...
- 简要讨论python对于1688的关键字搜索、商品详情在电商运营大数据分析、电商选品、竞品分析上的帮助
目录 1688商品详情简要描述 1688商品详情请求URL 请求方式 参数 关键信息 淘宝天猫以及1688.京东.拼多多平台不断地在改变人们的消费习惯,人们从传统购物模式不断走向线上模式,作为电商行业 ...
- 跨境电商商城独立站_是什么?
跨境电商商城独立站是一种在线零售平台,它专门为跨境电商出口企业搭建的独立网站,独立站通常而言是源码商城.这些网站提供的服务包括商品展示.在线交易.物流跟踪和支付等功能.在跨境电商行业中,这种独立站的重 ...
- 电商网站Web自动化测试实战( 编写京东搜索脚本python+selenium框架)
电商网站Web自动化测试实战( 编写京东搜索脚本) 1,打开京东页 京东首页地址:https://www.jd.com/,故进入京东首页如下: 2,打开浏览器开发者模式 定位元素前需先打开浏览器开发者 ...
最新文章
- SSIS中的记录集目标
- AnalogClock的使用(二):配合数字时针
- jQuery的DOM操作
- 解决ubuntu上在androidstudio中启动emulator闪退的问题(1)
- 从零开始,CentOS6安装ghost博客
- 双屏怎么快速切换鼠标_双屏触摸一体机功能特点解析
- Ardunio开发实例-光敏电阻光线强度检测
- 多标签文本分类/三种神经网络/网络模型
- android+锁屏显示农历,在手机锁屏界面上显示农历日期和天气
- 虚拟盘客户机文件盒服务器不一样,VMware ESX三种虚拟磁盘类型分析
- Ansible自动运维工具
- 【Comsol学习】二维非稳态热传导问题
- linux用户管理(1)----创建用户(adduser和useradd)和删除用户(userdel)
- word如何转excel
- linux CPU降频脚本
- nacos get changed dataId error, code: 403
- 【电源设计】06正激式开关电源
- AVM环视:一.相机标定及原理
- CleanMyMac x2021中文版mac清理软件激活码下载
- google网页翻译使用不了的解决方案
热门文章
- 2010年下半年计算机专业技术资格考试工作安排
- linux的HAL库函数,STM32 HAL库 IIC 协议库函数
- iOS开发 CGAffineTransform 让图片旋转, 旋转后获得图片旋转的角度
- linux自学(四)之开始centos学习,网络配置
- linux oracle手动启动两个实例
- poj--2019 Cornfields 2维RMQ
- C# 数据类型及相互转换及常用基础
- google天气预报接口_将天气预报添加到谷歌浏览器
- apt-get更新软件包_如何使用Apt-fast加速软件包下载和更新
- 大白话5分钟带你走进人工智能-第二十节逻辑回归和Softmax多分类问题(5)