为什么我的IIS7应用程序池在ASP.NET页面调用的DLL发生异常后会关闭?

24

我已经阅读了 ASP.NET应用程序池关闭问题IIS 7.5:应用程序池问题,但它们没有回答我的问题。

我有一个C# ASP.NET页面,在代码后端实例化了通过BIN目录提供的DLL的类,然后调用此实例上的方法。 DLL内部的方法由于DataRow对象中不存在的列而引发System.ArgumentException。事件日志显示以下错误:

Source: ASP.NET 2.0.50727.0
Application ID: /LM/W3SVC/1/ROOT/...
Process ID: 9476
Exception: System.ArgumentException
Message: Column 'someColumn' does not belong to table.
StrackTrace: 

ASP.NET页面中的调用代码在一个通用的try-catch块中包装方法调用。当我请求该页面时,这会导致我的IIS实例对应的应用程序池崩溃,并且我的网站不再可用(错误503)。我必须手动重新启动应用程序池,然后网站才能正常工作。

更新 根据请求,以下是来自ASP.NET代码后台的try catch块:

try
{
    SomeExternalClass someExternalClass = new SomeExternalClass();
    someExternalClass.SomeMethod( someId );
}
catch( Exception ex )
{
    // "smp" is an instance of "StatusMessagePanel", a control we use on all pages 
    // to show error information, basically a div container with an icon.
    smp.ShowError( ex.Message ); 
}

现在我的问题是,为什么尝试访问不存在的DataRow列时抛出一个相对“简单”的异常,如System.ArgumentException,会导致整个网站崩溃? ASP.NET页面的通用try-catch块也没有帮助,这不应该是完全使整个网站不可用的原因,或者这是错误的假设吗?我从来没有想过这会基本上使服务器崩溃。

预先警告一下,有人可能会告诉我应该在访问列之前检查列是否存在:我知道这一点,而且旧代码现在已经被更改了,但这不是我上面描述的问题,我想知道为什么后果如此严重。

更新2

在DLL中调用的方法启动了一个线程,该线程包含一个try-catch块:

[...]
try
{
    ThreadStart starter = () => CreateReport(...)
    Thread thread = new Thread( starter );
    thread.Start();
    if( !thread.Join( TimeSpan.FromMinutes( 15 ) ) )
    {
        // Log some timeout warning
    }
    else
    {
        // Log information about successful report generation
    }
}
catch( Exception ex )
{
    // Log error information
}

在 catch 块中会发生什么?如果那里抛出异常,你可能会遇到麻烦。你能发布 try catch 代码吗? - levelnis
捕获块调用一个方法,该方法仅将错误消息显示给客户端(浏览器),我会更新我的问题。 - Gorgsenegger
如果您完全删除try-catch块并直接调用方法,应用程序池是否仍会崩溃? - levelnis
回到工作时会尝试 :-) - Gorgsenegger
与之前相同。在ASP.NET页面的代码后面删除异常处理会导致应用程序池关闭。 - Gorgsenegger
显示剩余5条评论
2个回答

22
很可能你的应用程序池被IIS的快速故障保护功能自动关闭,正如你在链接的问题中所提到的。如果你检查事件日志,可能会发现连续抛出多个未处理的异常。
简而言之,在可配置的时间段内(默认为5分钟内5次),足够多的未处理异常将关闭应用程序池,导致503服务不可用响应。
这个功能背后的理由是,如果你有一个有故障的应用程序,你可能不想让它在每个后续请求中自动重启,消耗资源并可能破坏数据。
我必须承认,这也不是我期望的“默认”行为。
看一下Rick Stahl的解释,它更加深入。
为了真正解决这个问题,你需要捕获异常或者防止异常被抛出(就像@leppie建议的那样)。未处理的异常会导致整个执行进程崩溃(指单个请求/工作进程,而不是IIS),这使得.Net代码更容易进行调试,因为它不会隐藏错误或者仅仅挂起应用程序。
请注意,这在.Net 2.0中已经改变: http://msdn.microsoft.com/en-us/library/ms228965.aspx http://support.microsoft.com/kb/911816 更新: 根据你上面的更新,我认为如果从CreateReport()抛出异常,实际上并没有捕获它。该方法在一个单独的线程上执行:

exception still thrown

如果CreateReport()中还没有try-catch语句,你需要在其主体中添加一个:
public static void CreateReport() {
    try {
        throw new Exception("reducto");
    } catch {
        Console.WriteLine("done did.");
    }
}

感谢提供更详细的信息,尽管我仍然不明白为什么在 DLL 内部被捕获的异常会导致应用程序池关闭。多个异常快速连续抛出也不应该是问题,因为 DLL 方法的执行会在第一个内部异常被抛出时终止。事件日志还显示只生成了一个异常。 - Gorgsenegger
谢谢,我会尽快验证,只要我有时间 - 我们刚刚将我们的TFS迁移到不同的网络上,并且仍在重新恢复其速度的过程中,所以抱歉让您等待了,还请再等我几天。 - Gorgsenegger
1
确实有点难找。在那个方法中有一个 try catch 块,但它只捕获了一种(不同的)异常类型,在我描述问题时并没有发生。再次感谢 :-) - Gorgsenegger

4

这种情况曾经发生在我身上。实际错误(在我的情况下)是堆栈溢出,导致池关闭。

看起来像是IIS在保护自己免受消耗过多资源的影响。

我使用DebugDiag找到了问题。

以下是我的起点:http://www.webdebug.net/index.php/2012/12/collect-iis-crash-dump-with-debugdiag/

我想了解为什么外部DLL中的异常会导致IIS应用程序池关闭,即使异常在DLL内部被捕获,在ASP.NET页面的代码后台调用DLL方法时也是如此。

外部dll也在您的应用程序池中运行。此dll中的重大崩溃也会使您的应用程序池崩溃。有些异常无法处理,而stackoverflow异常就是其中之一。主题在这里中讨论。也许这就是在您的情况下发生的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接