现场问题处理记录

上周五经历了一场半夜起来处理现场问题的事,从早上1点多搞到4点多。总结一下。

遇到现场问题的时候:尽可能的保存现场很重要。打dump,查活动进程,看当前运行SQL,当前具体环境信息。在保存好现场之后可以从如下三个方面来提问:

  1. 是否是最近修改代码问题,尽快回滚最近发布
  2. 是否环境问题,最近是否有硬件升级。能否创建新实例来隔绝可能的环境问题
  3. 是否数据问题,包括脏数据,有问题的配置等

问题

出现问题的时候,在重新Recycle IIS的Application Pool之前,先dump了w3wp.exe进程,通过下面的Dump分析找到了出问题的URL:

通过观察URL,找到了出问题的对应物件的ID,原因是这个ID的ParentID是其自身,所以造成了循环引用。属于数据问题。又通过!wdo HttpContext来查看HttpContext找到了对应的用户信息。

进一步思考

为什么一个死循环会卡死整个应用?IIS不是给每个request新建一个Context然后放到独立的线程中运行的吗?理论上即使上面有问题的ID卡死了当前线程,那么接下来的request应该能得到正常执行,而不是卡死。

最终通过调查找到原因:在应用程序代码使用Application.Lock()而忘记释放导致的。最终的释放是IIS在Pipeline处理完成后自动无条件释放所有锁,所以卡顿的request结束后,接下来的request才有机会继续执行。

iis_auto_release_all_lock.jpg

.NET中的死锁分析

死锁简单来说就是:想要的得不到

通常有两种情况造成死锁:

对于循环持有锁的情况,在WinDbg中加载SOSEX,再使用!dlk命令会自动搜索这种死锁。

对于锁着不放情况,调查死锁的方法是:

  1. 找到等待/持有这个锁的线程,可以使用~*e!clrstack查看所有线程调用栈,通常是卡在System.Threading.Monitor类型Enter/Wait方法上。
  2. 如果是等待锁,那么谁持有这个锁?可以通过
    • 查看当前线程的调用栈和参数!clrstack -p,哪个对象在获取锁,!do 查看这个对象能得到更多信息。比如HttpApplicationStateLock会记录当前持有者的线程id。
    • 或者!SyncBlk -all看看你关注持有锁的对象。
  3. 通过~[thread id]s跳掉持有锁的线程,看看当前线程在做什么,为什么不释放,通常到这里就找到问题了。
@ 2019-11-14 11:02

Comments:

Sharing your thoughts: