Я все еще пытаюсь понять текущую проблему, но ее можно в значительной степени суммировать как невозможность выгрузки AppDomain .
Это происходит во время развертывания ASP.NET WebAPI в службе приложений Azure, и мы наблюдаем следующее:
- Идентификатор процесса не изменяется , новое развертывание размещается в том же процессе (AFAIU выполняется через выгрузку старого AppDomain и раскрутку нового AppDomain с обновленными двоичными файлами)
- Диагностика Azure PaaS показывает следующее в разделе ошибок:
"В w3wp_12396.dmp HttpRuntime для приложения
/ LM / W3SVC / 1523308129 / ROOT равен в середине останова . "
Анализируя дампы памяти, мы видим потоки с установленным флагом IsAbortRequested , но они, похоже, никогда не заканчивают (вывод WinDbg !threads
здесь: https://pastebin.com/7CXYcffy)
В дампах памяти мы также видим множество доменов приложений со стадией " UNLOAD_REQUESTED ", они, похоже, никогда не завершают выгрузку (полный вывод !DumpDomain
здесь: https://pastebin.com/kahZQuWN)
Domain 7: 000001c67062c800
LowFrequencyHeap: 000001c67062cff8
HighFrequencyHeap: 000001c67062d088
StubHeap: 000001c67062d118
Stage: UNLOAD_REQUESTED
SecurityDescriptor: 000001c6705c5680
Name: /LM/W3SVC/1523308129/ROOT-6-131687140950004974
Не обнаружено взаимоблокировок (по крайней мере, с помощью команды !dlk
плагина WinDbg SOSEX, которая обычно охватывает большинство случаев взаимоблокировки)
Нет кода отменяет прерывание потока (нет Thread.ResetAbort()
вызывается)
Единственный способ решить проблему сейчас - это остановить процесс (остановить Azure AppService).
Каковы возможные причины невозможности выгрузки AppDomain?
UPDATE . В стеках потоков мы получили подсказку, что это может иметь отношение к нашему пользовательскому приложению 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) ...