老峰的博客
=技术 ?? new 技术()

导航

 

  背景:生产环境中升级最新版本后,系统中最重要的一个Windows 服务在某种情况下会异常退出。查看日志,只是模糊的说在一个Oracle DataAccess发生了异常。

  开始动手吧。

  1. 用adplus来捕获异常模式

    adplus -crash -pn PayMedia.Background2.WindowsService.Billing.exe -o c:\crashdump

   其中PayMedia.Background2.WindowsService.Billing.exe是Window Service的名字

  2. 耐心等待系统崩溃.......

  3. 获得Dump 文件,在开发机器用Windbg打开dump文件,

  在Windbg中运行:

.loadby sos mscorwks -- 用.loadby sos clr替代如果你的程序运行在.net 4.0上
~*e!clrstack

如何使用Windbg命令,请详细参见http://support.microsoft.com/kb/892277

  在这个个案中,获得了3个dump文件,这里就只截取其中有问题的一部分:

发现其中有一条“HandleOracleException”,堆栈往上看,马上我们代码中实际有问题的函数名字。

在原代码中快速查找,发现如下代码:

public void UpdateStatus(IBackgroundService backgroundService)
{
using (IDataAccess da = DataAccessFactory.CreateDataAccess("SYSTEMDATA", "SYSTEMDATA"))
{
if (backgroundService.Id == 0)
{
backgroundService.Id
= GetServiceId(backgroundService, da); // scope
}

if (backgroundService.Id == 0)
{
InsertService(backgroundService, da);
// scope
backgroundService.Id = GetServiceId(backgroundService, da);
}
else
{
UpdateService(backgroundService, da);
// scope
}

}
}

private static void UpdateService(IBackgroundService backgroundService, IDataAccess da) // DataAccessScope scope
{
da.Parameters.Add(
"status", backgroundService.Status);
da.Parameters.Add(
"id", backgroundService.Id);

da.ExecuteNonQuery(SQL_UPDATE_SERVICE);
}

很明显我们看到代码没有处理任何异常,所以我们的错误信息没有很好的被log。如何处理异常是体外话,我们要找到问题的根源。我们快速的改一下代码把异常log下来。

public void UpdateStatus(IBackgroundService backgroundService)
{
try
{
using (IDataAccess da = DataAccessFactory.CreateDataAccess("SYSTEMDATA", "SYSTEMDATA"))

{
if (backgroundService.Id == 0)
{
backgroundService.Id
= GetServiceId(backgroundService, da); // scope
}

if (backgroundService.Id == 0)
{
InsertService(backgroundService, da);
// scope
backgroundService.Id = GetServiceId(backgroundService, da);
}
else
{
UpdateService(backgroundService, da);
// scope
}


}
}
catch (Exception e)
{
Logger.Write(Logger.Category.Exception, e.Message, TraceEventType.Critical, e);
throw e;
}

}

替代生产环境中的程序,等待再次crash。很快我们在Event Log中发现了问题的本质线索:

Failed to execute non-query. (ORA-02290: check constraint (SYSTEMDATA.CK_BACKGROUND_SERVICE_7) violated) --> Failed to execute non-query. (ORA-02290: check constraint (SYSTEMDATA.CK_BACKGROUND_SERVICE_7) violated) --> ORA-02290: check constraint (SYSTEMDATA.CK_BACKGROUND_SERVICE_7) violated

继续查看这个数据的Constraint

ALTER TABLE SYSTEMDATA_EDB.BACKGROUND_SERVICE ADD (
CONSTRAINT CK_BACKGROUND_SERVICE_7
CHECK (REQUESTED_STATUS in (0,1,2)));

根据业务规则,这个Status是用来标识我们背景线程的状态的,在代码中有5中状态,可能由于更新不一致问题,数据库没有及时更新。

2011-05-31更新

在步骤3中用~*e!clrstack列出所有线程的堆栈是然后人肉看这些线程是非常无聊的,在这里我有另外一个更好的命令(真是不学累死人):

!pe

这样我们得到的知识有异常的线程堆栈

posted on 2011-03-04 09:45  iVen Xu  阅读(672)  评论(1编辑  收藏  举报