您的位置:首页 > 其它

不用windbg解决High CPU的一个案例

2008-04-17 13:47 274 查看
场景:一个400-700 requests /sec的web app再一次较大改版后遇到了high cpu的问题,当负载攀高时系统相应开始变慢。

基础:本文着重在于讲述分析问题的过程。lixiong写的windows用户态程序高效排错一书从一开始就在讲述一个道理:找到问题的关键不在使用windbg的过程,而在于对问题的分析。

首先,这是一个用于web聊天和消息投递的web应用程序,所以一般情况下不存在对大数据的传输和处理。cpu在75-90%之间波动。而且程序使用了Lion的异常捕获组件,也就是说所有能看到的asp.net的导致yellow screen of death的情况,是都能捕获到的。那么asp.net的程序出问题了,就先从perfmon里看看情况。

打开perfmon,添加了几个counter,等一会,看看平均值:

Requests/sec:450

Request in application queen:0(没有hang)

Virtual Bytes和Private Bytes都平稳,两者关系大概为4倍大小。

# Bytes in all heaps:波动较剧烈

......

......

web系统响应慢,一般我会先考虑是不是有什么lock导致的hang的情况,大家都在等,从而导致Request in application queen值不为0,当这个值继续增大的某个程度的时候,访问程序就直接扔给你Error 503了。但是这里这个值为0,说明hang的可能性太小了。

那Virtual Bytes=4倍大小的Private Bytes,这个看起来有点问题,通常来说,Virtual Bytes不会大于2倍大小的Private Bytes,否则应该考虑是不是有fragment的问题了,检查web.config,debug=false没问题,这时候觉得fragment的可能性也比较小了。

# Bytes in all heaps这个值应该是只有GC在collect的时候才会变化,但是Virtual Bytes和Private Bytes都是平稳的,memory leak的可能性基本没有,程序也没有使用有特别问题的非托管代码。那# Bytes in all heaps活动比较频繁,是为什么?

既然常用的counter不能解释更多问题了,就再加上了几个与.net相关的计数器。一下子# of exceps thrown / sec的值让我大吃一惊:这个程序每秒钟抛出480个异常!

# of exceps thrown / sec计数器是指示每秒种出现的异常的个数,这里的异常包括已经catch到的。一般来说该值在20一下是正常的。前文已经提到过我并没有log到这么多的exception,也就是说这些exception全部是.net或asp.net抛出来的。这里我把.net和asp.net分开说是因为原因有2个:

如果.net平台调用的一些非托管资源返回一些不正确的HResult格式,那就会有一个异常出来。
asp.net框架在某些地方也会抛一个异常,但是自己就处理了。只不过我们没有注意到。

OK,先不急着去抓dump找具体原因,继续看perfmon。删除掉一些counter,保留几个,调整一下颜色和比例后观察一段时间:CPU、异常个数和请求数的趋势大致相同,差值也基本恒定,那至少我们可以看出:

cpu只所以这么high,exception功不可没。
几乎每个请求都会抛出异常,但肯定不是所有的请求都抛出异常(否则应该是接近正比关系)
总有一些请求每次抛出不止一个异常(还是通过不是正比关系来判断)

往往一个大型的web应用程序只有固定的几个页面会有较大较频繁的访问量(当然在spider活动不频繁的时段就是特殊情况了)。什么样的代码会这么频繁被调用?通常来说三种可能性:HttpMoudle、HttpHandler和像自定义的Page基类一样的东西。

按照顺序逐个去找,最好找到了一个地方,在HttpHandler里使用了context.Server.Transfer()方法。HttpServerUtitility类的Transfer()方法用Reflector来看一下:

1public void Transfer(string path, bool preserveForm)

2{

3{

6 throw new ApplicationException(SR.GetString("Transfer_not_allowed_in_callback"));

7 }

8 this.Execute(path, null, preserveForm);

9 this._context.Response.End();

10}
看到问题了吧:如果handler有问题,那么throw一个ApplicationException出来,否则调用了Response.End()方法,而Response.End()也会throw一个:(感谢overred指正)

1 public void End()

2 {

3 if (this._context.IsInCancellablePeriod)

4 {

5 InternalSecurityPermissions.ControlThread.Assert();

6 Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));

7 }

8 else if (!this._flushing)

9 {

10 this.Flush();

11 this._ended = true;

12 if (this._context.ApplicationInstance != null)

13 {

14 this._context.ApplicationInstance.CompleteRequest();

15 }

16 }

17 }
这些handler都是在处理那些被频繁请求的页面,一个请求就至少有2个异常。
这里想留下两个要点一起与大家讨论:

为什么CPU高?撇开其他部分不说,创建exception,把callstack和那些数据收集起来是很昂贵的操作。之后GC有要负责清理这些,也是expensive way。
一开始就提到了Virtual Bytes和Private Bytes的比例是在4倍左右,Virtual Bytes为为什么这么高?仅仅因为cpu多吗?(在taskmgr里可以看到4 cpu,x86)

请您一定留下宝贵的想法和分析思路。谢谢您。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐