c# - IIS 应用程序池在中间件记录 OperationCancelledException 约 20 秒后崩溃
问题描述
在我们的生产服务器上,全天间歇性地,我们看到这个被记录下来:
为应用程序池“MyApi”提供服务的进程与 Windows 进程激活服务发生了致命的通信错误。进程 ID 为“29568”。数据字段包含错误号。
我已经看到解决此问题的建议,例如在 IIS 中设置一些 32 位的东西,但我无法在生产级别控制 IIS。另外,我的公司运行着几十个应用程序池,只有我正在调查的一个有这个问题。所以我已经排除了某种 IIS 配置问题。
我已经通过 DebugDiag 运行了故障转储,这就是报告的内容:
在 w3wp.exe.18080.dmp 中,来自 Microsoft Corporation 的 C:\Windows\System32\inetsrv\iiscore.dll 中 iiscore!W3_CONTEXT_BASE::GetIsLastNotification+62 处的汇编指令在尝试读取时导致访问冲突异常 (0xC0000005)线程 94 上的内存位置 0xd7f76008
我试过谷歌搜索 GetIsLastNotification 并在 MS 文档中找到了这个:
不要将 PreSendRequestHeaders 与实现 IHttpModule 的托管模块一起使用。设置这些属性可能会导致异步请求出现问题。应用程序请求路由 (ARR) 和 websocket 的组合可能会导致访问冲突异常,从而导致 w3wp 崩溃。例如,iiscore.dll 中的 iiscore!W3_CONTEXT_BASE::GetIsLastNotification+68 导致了访问冲突异常 (0xC0000005)。
它说不要将 PreSendRequestHeaders 与实现 IHttpModule 的模块一起使用。我已经验证了整个应用程序没有代码这样做。我还验证了我们公司所有库中的代码都没有这样做。
这是一件非常有趣和不寻常的事情。每次应用程序池崩溃前大约 20 秒,我看到这个记录:System.OperationCanceledException: The operation was canceled.
我已确定此错误来自某些 Owin 中间件。在应用程序的Startup.cs
文件中,我们注册了一个执行一些日志记录的类。OperationCanceledException
由于以下代码,它记录了这一点:
// This class inherits from OwinMiddleware
public override async Task Invoke(IOwinContext context)
{
try
{
await this.Next.Invoke(context);
}
catch (Exception ex)
{
// log stuff
}
}
这里发生的所有事情都是当一个 http 请求被取消时,await.this.Next.Invoke(context)
抛出异常,因为这是它应该做的。这似乎没什么大不了的,但问题归结为:取消请求如何导致应用程序池在大约 20 秒后崩溃?
解决方案
经过一番努力,终于找到了答案。标题中提到的“20 秒”事情最终成为了一些延迟日志记录的红鲱鱼原因。但以下是应用程序池崩溃的原因。
在应用程序的 Startup.cs 文件中,我们注册了一些 Owin 中间件。中间件看起来像这样:
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
// log the error and return a 500 response
await LogAndRespond(context, ex);
}
}
这样做的问题是,当调用此 api 的客户端取消请求时,Next.Invoke(context)
会抛出一个OperationCanceledException
. 在 catch 块中,我们记录了这个错误,但更重要的是,我们返回了对取消请求的响应。
我不完全理解为什么这会导致整个应用程序池崩溃。我猜想中间件尝试响应关闭的连接会导致内存访问冲突。无论如何,解决方案是不发送响应。最终的代码最终看起来像这样。
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (OperationCanceledException)
{
// Log the canceled request as info, but do NOT send it a response
_logger.LogInformation("Request has been canceled: {Url}", context.Request.Uri);
}
catch (Exception ex)
{
// log the error and return a 500 response
await LogAndRespond(context, ex);
}
}
推荐阅读
- computer-vision - 将包含多边形的xml转换为yolo
- javascript - 使 postcss 与材料 ui 的 jss 一起工作
- javascript - 仅通过透明部分上传图片
- reactjs - 使用 Antd (Ant -design) React js 删除多选列表中的列表选择计数
- javascript - 在reactjs中没有触发有条件选择的select onChange(event)方法
- ibm-watson - IBM Watson Assistant - SpelParseException
- android - Delphi Android 应用程序为我提供了图库中图像的副本。我如何获得原件?
- spring - 在spring data jpa中,我们如何根据非主键的存在来保存或更新
- laravel - Using unique() method when generating a list of options in blade template
- nosql - Scylla certifications for admin/architects/developer