我仍在尝试了解持续存在的问题,但它几乎可以概括为无法卸载 AppDomain。
它发生在将 ASP.NET WebAPI 部署到 Azure App Service 的过程中,我们观察到以下情况:
- 进程 ID 不变,新部署托管在同一个进程中(AFAIU 是通过卸载旧 AppDomain 并使用更新的二进制文件启动新 AppDomain 来完成的)
- Azure PaaS 诊断在错误部分显示以下内容:
“在 w3wp_12396.dmp 中,应用程序 /LM/W3SVC/1523308129/ROOT 的 HttpRuntime 正在关闭中。”
分析内存转储,我们看到设置了IsAbortRequested标志的线程,但它们似乎从未完成(
!threads
此处 WinDbg 的输出:https ://pastebin.com/7CXYcffy )在内存转储中,我们还看到很多带有“ UNLOAD_REQUESTED ”阶段的 AppDomain,它们似乎从未完成卸载(完整输出在
!DumpDomain
这里:https ://pastebin.com/kahZQuWN )
域 7:000001c67062c800 低频堆:000001c67062cff8 高频堆:000001c67062d088 存根堆:000001c67062d118 阶段:UNLOAD_REQUESTED 安全描述符:000001c6705c5680 名称:/LM/W3SVC/1523308129/ROOT-6-131687140950004974
未检测到死锁
!dlk
(至少通过 WinDbg SOSEX 插件的命令,通常涵盖大多数死锁情况)没有代码取消线程中止(没有
Thread.ResetAbort()
调用)
我们现在可以解决问题的唯一方法是终止进程(停止 Azure AppService)。
AppDomain 无法卸载的可能原因有哪些?
更新。在线程堆栈中,我们得到一个提示,它可能与我们的自定义 Azure Blob Log4net 附加程序有关,我发现当创建此类附加程序时(每个应用程序一次),它会产生具有以下结构的新线程。
while (true)
{
try
{
Flush(); // pseudocode
Thread.Sleep(10000);
}
catch(Exception)
{
}
}
不确定我是否理解为什么它会导致完全无法停止的线程(因为ThreadAbortException
不会被 catch 停止),但它看起来像改变while (true)
以while (!Environment.HasShutdownStarted && !_stopping)
解决问题(在调用_stopping
Appender 时设置,OnClose
这是 log4net 的一种优雅关闭)......