.NET 4.6升级后w3wp.exe出现ThreadAbortException的间歇性崩溃

10

最近几天,我们发现公司网站的主应用程序池服务的w3wp.exe工作进程出现了间歇性崩溃。有时候崩溃是孤立的,并且IIS能够成功地重启工作进程。但是如果在5分钟内发生超过5次崩溃,则IIS的快速故障保护机制会启动并停止应用程序池。以下是崩溃前应用事件日志中的一个示例条目:

An unhandled exception occurred and the process was terminated.
Application ID: /LM/W3SVC/2/ROOT
Process ID: 3640
Exception: System.Threading.ThreadAbortException
Message: Thread was being aborted.
StackTrace:    at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)
   at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
   at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)

在由ThreadAbortException引起的崩溃之后,会记录更严重的事件:

Faulting application name: w3wp.exe, version: 8.0.9200.16384, time stamp: 0x5010885f
Faulting module name: KERNELBASE.dll, version: 6.2.9200.17366, time stamp: 0x554d16f6
Exception code: 0xe0434352
Fault offset: 0x00010192
Faulting process id: 0xe38
Faulting application start time: 0x01d100dc662652d6
Faulting application path: C:\Windows\SysWOW64\inetsrv\w3wp.exe
Faulting module path: C:\Windows\SYSTEM32\KERNELBASE.dll
Report Id: db5b0d5b-6cd0-11e5-9418-005056900458
Faulting package full name: 
Faulting package-relative application ID: 

现在,ThreadAbortException不应该导致w3wp.exe崩溃,因为它会在执行标准的Response.Redirect()时抛出。MSDN证实了这一点,我也通过简单测试进行了确认。然而,至少有一个人最近在类似的环境中看到了类似的崩溃:Thread.Abort in ASP.NET app causes w3wp.exe to crash。(但这可能是一个无关的问题。) 我们的环境:
  • 企业网站含购物车和合作伙伴网络服务,目标为.NET 4.5。(包括业务逻辑DLL和内部库在内的900,000多行自定义代码)
  • 2个VMWare Web服务器使用Windows NLB组成负载均衡池
  • IIS 8.0 / Windows 2012 Server Standard / .NET 4.6.00081
  • 应用程序池以32位模式运行,因为我们需要支持少量调用遗留VB6 DLL的经典ASP页面。

背景:

在崩溃开始的前几天,我们升级到了.NET 4.6。我们启用了新的RyuJIT(默认设置),并安装了所有更新以解决此处描述的关键编译器问题:http://blogs.msdn.com/b/dotnet/archive/2015/07/28/ryujit-bug-advisory-in-the-net-framework-4-6.aspx

我们部署了新版本的网页代码(每周进行多次)。自然地,我们对代码更改进行了双重检查,以防止可能存在的崩溃漏洞,但我们的更改似乎没有易受无限循环、递归堆栈溢出或高内存使用的风险——这些通常是w3wp.exe发生未处理异常时的罪魁祸首。
有时崩溃会在几分钟内影响到一个Web服务器,但其他时候只有一个Web服务器受到影响。
我尝试过以下方法:
- 重新启动服务器并安装所有Windows更新。 - 分析IIS日志,查看是否在崩溃之前有任何可疑/恶意请求进入。我找不到任何模式——所有请求都看起来很正常。 - 启用w3wp.exe的自动崩溃minidump(如https://msdn.microsoft.com/en-us/library/bb787181.aspx所述),并使用WinDbg分析它们。不幸的是,CLR“感兴趣的堆栈跟踪”没有显示任何有用的信息,只有几个与我们的代码无关的空GC帧:
> 0:026> !clrstack
> OS Thread Id: 0x1ff0 (26)
> Child SP       IP Call Site
> 2321f96c 771bdf8c [GCFrame: 2321f96c]
> 2321f9a4 771bdf8c [GCFrame: 2321f9a4]

有什么想法吗?

更新:

我们已经在Web服务器上回滚了.NET 4.6和最近的Windows更新。我们已经监视了2或3天,具体取决于何时回滚服务器,在每种情况下,尽管保持相同的应用程序代码,但都没有发生崩溃。这基本上证明了间歇性崩溃是由.NET 4.6或其他Windows更新引起的,而不是我们的代码,因为以前w3wp.exe每天会崩溃多次。

我们现在正在试图向Microsoft支持证明这一点,但这是一项艰巨的任务,因为该问题是随机的、间歇性的,我们无法可靠地重现它。(他们提供了一个dump analysis,但似乎是个红鱼)我们还在重新应用更新,并等待几天观察崩溃,以便确定有问题的更新。显然,这是一个繁琐的过程。

更新#2:

我们现在已经重新应用了所有在故障排除中被删除的.NET 4.6之前的Windows更新,服务器已经运行了几天而没有崩溃。唯一剩下的需要重新应用的是.NET 4.6及其自身的更新,但我的管理层理所当然地不愿意安装可能会在生产中导致崩溃的东西。因此,我继续与微软合作分析不同的崩溃转储以确定问题所在。

1
你在网站代码中手动启动任何线程吗? - mason
1
任何与HTTP请求无关的线程中的异常都会终止进程。我敢打赌这与.NET 4.6无关,那可能只是巧合。你不应该自己启动线程。根据任务的持续时间,您可以使用基于任务的异步编程,或者转移到其他方法来在后台运行该代码。请参阅Phil HaackScott Hanselman的博客文章。 - mason
@mason 很高兴在聊天中继续讨论,但是那些博客文章并没有真正否定MSDN。它们只是省略了关于生成线程上的 ThreadAbortExceptions 的特殊情况,这不会使进程崩溃。这里有一个非常简单的测试,证明了MSDN是正确的:http://pastebin.com/dtzkE3gG。所以针对我的问题:1)生成的线程上的 ThreadAbortExceptions 不应该引起问题;2)即使引起了问题,我也应该看到指向用户代码的堆栈跟踪,但我没有看到。 - Jordan Rieger
1
你尝试过在整个计算机上禁用RyuJIT吗?我们曾经遇到过一些有趣的问题。 - Simon Mourier
显示剩余12条评论
3个回答

5
你没有提供任何代码,但证据表明这是你的应用程序代码的问题,而不是.NET 4.6或特定于ThreadAbortException的问题。
基本的故障排除步骤:你说有代码更改和环境更改;因此,请从中排除一个。
  • 在安装了.NET 4.5的虚拟机上测试应用程序。如果没有错误,则可能是.NET 4.6的原因。

  • 在同一服务器上测试旧版本的应用程序。如果没有问题,则代码更改很可能是原因。

  • 在安装了VS.NET的计算机上测试应用程序,附加到w3wp.exe进程并进行调试(工具>附加到进程)。捕获ThreadAbortException并跟踪它。

  • 如果您还没有这样做,您应该记录w3wp.exe进程停止的事件...尽管这显然不能处理所有异常。搜索一下谷歌,但这个家伙描述了一个我也使用的解决方案

  • 如果您还没有这样做,请在Global.asax中定义一个Application_Error处理程序来记录异常。Microsoft演示了这一点。创建一个System.Web.Configuration选项,您可以在其中切换web.config文件以启用不同级别的日志记录,包括写入本地文件和写入Windows事件日志等。您还可以安装类似于Elmah的日志记录处理程序工具。

  • 创建一个最简单的Web应用程序,并测试Response.Redirect以验证是否会导致.NET 4.6崩溃的w3wp.exe(工作进程)。我做过这件事,它没有这样做,所以我怀疑是你的代码。或者可能是奇怪的服务器/修补程序级紧急情况..这些步骤应该帮助您确定问题。

另外注意:即使它不应该影响应用程序进程,我建议解决Response.Redirect()问题。我们最近在企业应用程序中做到了这一点,确实是一个广泛范围的更改,但我们不再出现TAE异常。修复很简单:只需调用Response.Redirect(false);,然后确保在调用该函数之后不会运行任何代码(例如调用return)。这篇文章解释了

1
昨天我们在其中一台Web服务器上回退到.NET 4.5(但仍使用我们最新的代码)。到目前为止,该服务器尚未崩溃 - 这强烈表明.NET 4.6有问题,但在没有崩溃的情况下,我无法确定,因为崩溃是随机的,无法按需重现。我们已向Microsoft支持提供了崩溃转储,但他们的分析到目前为止似乎没有帮助。Response.Redirect()可能与我们的问题无关,因为转储中的CLR堆栈跟踪指向控件呈现代码中的无限循环。 - Jordan Rieger
你的应用程序中有递归函数吗?几个月前我也遇到了同样的问题,后来发现实际问题出在我的代码上而不是服务器上(即意外情况导致无限循环)。nothingisnecessary的答案似乎是正确的。 - user890255
1
因此,在所有服务器上回滚了.NET 4.6(以及一堆无关的Windows更新)之后,就再也没有崩溃了。这些服务器已经回滚了2到3天。我们的应用程序代码没有改变。这相当明确地证明,要么是.NET 4.6或其他Windows更新导致了间歇性崩溃,而不是我们的代码,因为w3wp.exe以前每天会崩溃几次。我们现在正在尝试向Microsoft支持证明这一点,但这是一场艰苦的战斗,因为问题是随机的、间歇性的,我们无法可靠地重现它。 - Jordan Rieger
感谢提供详细信息。同意你的证据可能表明.NET 4.6和Server 2012的组合存在问题。我们也计划将其移植到RyuJIT和4.6,用于64位Web应用程序,因此我想事先了解这个问题的原因。但是,在我们的测试环境中,我们没有注意到这个问题,所以我想知道是否引入更多负载会触发它... 我会及时向您报告。 - nothingisnecessary
我在Server 2008R2上遇到了类似的问题。据我所知,这不仅仅是ThreadAbortExceptions的问题;每当应用程序(在这种情况下是DotNetNuke)抛出任何未处理的异常时,它都会导致整个AppPool崩溃。 - MisterZimbu
显示剩余5条评论

3

@Jordan Rieger,这个bug应该在.NET 4.6.1中得到修复。请确认新框架是否已经解决了问题?还是问题仍然存在?谢谢。


看起来.NET 4.6.1已经解决了这个问题,因为我们已经安装了几周,没有遇到这个问题。从.NET 4.6回滚到4.5也暂时为我们解决了这个问题,但我很高兴现在使用的是最新的稳定版本。 - Jordan Rieger

0

我们正在考虑回退到4.5.x版本或禁用RyuJIT,但根据微软的说法,他们已经解决了Nick Craver和Marc Gravel发现的问题,正如我在问题中提到的,我们已安装该更新: http://blogs.msdn.com/b/dotnet/archive/2015/07/28/ryujit-bug-advisory-in-the-net-framework-4-6.aspx。 - Jordan Rieger
已经提及,但目前没有证据表明4.6是稳定的,例如没有其他问题。 - Dexion

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