AppDomain, обрабатывающий исключения
Я разрабатываю большое приложение, которое состоит из множества небольших плагинов/приложений.
Они недостаточно велики, чтобы быть полноценным процессом, но слишком малы для запуска в потоке, в рамках одного процесса, вместе с тем, что я хочу использовать его на основе плагина.
Если доступна более новая версия этого плагина, она должна быть выгружена, обновлена и запущена снова.
Во время поиска решения я могу воспользоваться волшебным словом AppDomain, и я цитирую:
"Использовать домены приложений для изоляции задач, которые могут обработать. Если состояние AppDomain, выполняющего задачу, становится нестабильный, AppDomain может быть выгружен без ущерба для процесса. Это важно, когда процесс должен выполняться в течение длительного времени без повторный запуск. Вы также можете использовать домены приложений для изоляции задач, которые не должны делиться данными.
Таким образом, это именно то, что я хочу. Однако, я думаю, их "состояние становится неустойчивым" - это другая точка зрения, чем моя. Я думаю о проблеме, когда один из плагинов генерирует исключение по любой причине. Я бы хотел, чтобы это было уловлено, отправлено по электронной почте, выгружено и перезагружено (если возможно).
Итак, я создал приложение, которое запускается, ищет всю DLL в своей папке. Проверяет, содержит ли DLL плагин. Создает новый AppDomain для этого плагина, и как только все будет загружено, он запустит каждый плагин. (Где каждый плагин может состоять из нескольких потоков, совместное существование рядом с ech другим).
Итак, я также добавил тайм-аут, который срабатывает через 5 секунд, чтобы выбросить новый Exception(); Добавлено событие UnhandledException в AppDomain для его обработки. Но он поймал его, и после каттинга все еще "разбил" весь процесс, включая все дополнительные дочерние объекты AppDomains.
Но в цитате четко указано, что изолировать задачи, которые могут "сбить процесс". Так что я пропущу что-то жизненно важное? Является ли мой взгляд на цитату неправильным?
Ответы
Ответ 1
Так как .NET 2.0 необработанные исключения разбивают процесс. Из AppDomain.UnhandledException документация по событиям:
Это событие предоставляет уведомление о неперехваченных исключениях. Это позволяет приложение для регистрации информации об исключении перед системой обработчик по умолчанию сообщает об исключении пользователю, а завершает приложение.
То же самое относится к AppDomain.FirstChanceException:
Это событие только для уведомления. Обработка этого события не обрабатывает исключение или повлиять на последующую обработку исключений любым способом.
Вам нужно подумать о том, как вы будете обрабатывать исключения, точно так же, как вы это сделаете в обычном приложении. Просто использование AppDomains не поможет. Если исключение не было обработано в пределах данного AppDomain, оно будет возвращено при вызове AppDomain, пока оно не будет обработано или не завершится сбой процесса. Совершенно нормально обрабатывать некоторые исключения и не позволять им разрушать ваш процесс.
AppDomain - это логический контейнер для сборок и памяти (не для потоков). Изоляция для AppDomain подразумевает:
-
Объекты, созданные в домене A, не могут быть доступны напрямую доменом B (без маршалинга). Это позволяет разгрузить домен A, не затрагивая ничего в домене B. Эти объекты будут автоматически удалены, когда домен "владеющий" будет выгружен.
-
Ассембли можно автоматически выгружать с помощью AppDomain. Это единственный способ выгрузить управляемую dll из процесса. Это полезно для горячей замены DLL.
-
Разрешения и конфигурация безопасности AppDomain могут быть изолированы от других приложений AppDomains. Это может быть полезно при загрузке ненадежного стороннего кода. Он также позволяет вам переопределить, как будут загружаться сборки (привязка к версии, теневое копирование и т.д.).
Наиболее распространенные причины использования AppDomain - это когда вы запускаете ненадежный сторонний код. Или у вас есть неуправляемый код и вы хотите разместить CLR или использовать горячую замену dll. Я думаю, что в CLR хостинг сценарий может сохранить ваш процесс от сбоя, когда код третьей стороны выдает необработанное исключение.
Вместо того, чтобы сворачивать свою собственную инфраструктуру, вы можете посмотреть System.Addin или MEF.
Ответ 2
Есть две проблемы с необработанным исключением. AppDomain решает только один из них. Вы пытаетесь разобраться с другим.
Хорошие новости. Когда вы обрабатываете исключение, вам нужно восстановить состояние программы, как будто исключение никогда не происходило. Все должно быть перемотано в состояние, находящееся перед кодом. Обычно у вас есть куча catch и finally clauses, которые отменяют мутации состояния, выполняемые кодом. Конечно, ничего очень простого. Но совершенно невозможно, если исключение необработанно. Вы не знаете, что именно получилось, и как его восстановить. AppDomain обрабатывает эту очень сложную проблему с апломбом. Вы выгрузите его, и все, что осталось от вас, просто исчезло. Больше нет мусора, собранного кучи, больше кучи загрузчика (статика). Вся энчилада получает reset независимо от того, что было до создания AppDomain.
Это здорово. Но есть и другая проблема, с которой довольно сложно справиться. Вашей программе было предложено выполнить задание. Поток отправился выполнять эту работу. Но он перенес сердечный приступ. Большая проблема номер один: поток мертв. Это очень плохая новость, если для вашей программы был только один поток. Там нет нити, программа завершается. Приятно, что AppDomain выгружается первым, но это действительно не имеет никакого значения, он все равно выгрузился.
Большая проблема: очень важно, чтобы эта работа была выполнена. Это не так. Это важно, работа была, скажем, балансировать корпоративные отчеты о прибылях и убытках. Это не получилось, кому-то придется позаботиться об этом, потому что не балансируя это выражение, многие люди будут очень расстроены.
Как вы решаете это?
Есть только несколько выбранных сценариев, где это приемлемо. Серверные сценарии. Кто-то просит его что-то сделать, сервер сообщает "не мог", обратитесь к системному администратору ". Работа ASP.NET и SQL Server. Они используют AppDomains для поддержания стабильности сервера. И иметь системных администраторов для решения проблем. Вам нужно будет создать такую систему поддержки, чтобы заставить AppDomains работать для вас.
Ответ 3
Просто добавив дополнительную информацию по этому вопросу для любого, кто считает (был там сам), используя области приложений, главным образом, чтобы гарантировать стабильность приложения:
Несколько лет назад команда System.AddIn
опубликовала очень интересную запись в блоге. Использование изоляции AppDomain для обнаружения ошибок надстройки.
В нем объясняется, что только надстройки вне процесса могут гарантировать стабильность хоста. Более конкретно:
Начиная с CLR v2.0 необработанные исключения для дочерних потоков будут теперь вызывают весь процесс и, следовательно, невозможно чтобы хост полностью восстановился после этого.
Так что они предлагают подписаться на AppDomain.UnhandledException и до того, как ваше приложение выйдет из строя, сохраните где-нибудь (журнал, базу данных и т.д.) информацию о том, кто вызвал это исключение. Затем в следующий раз, когда ваше приложение начнет использовать эту информацию для защиты вашего приложения. Возможно, вы не загружаете надстройку или не сообщаете пользователю и не позволяете ему/ей решать. (Приложения Microsoft Office следовали этому подходу и отключили плагины, которые разбивали хост. Затем вам пришлось повторно активировать их самостоятельно.)
Они также опубликовали еще одну запись в блоге, в которой показано, как это сделать даже в сценариях, где хост работает на другом хосте (IIS, WAS и т.д.).
Подробнее о регистрации необработанных исключений из управляемых надстроек.
Хотя обе эти статьи сосредоточены вокруг System.AddIn
, они содержат полезную информацию для тех, кто пытается повысить стабильность своего приложения, поддерживающего плагин.
Ответ 4
AppDomain более часто используется для того, чтобы иметь возможность разгружать сборки (например, предлагаемые вами) и управлять параметрами запуска, такими как уровни доступа .NET, конфигурации и т.д. Если вы действительно хотите "изоляцию", лучшим выбором всегда будет рабочий процесс; однако, это намного больше работы.
Я делаю это в нескольких проектах. Чтобы дать широкомасштабную картинку, мы используем Google ProtoBuffers (Jon Skeet порт) через управляемую библиотеку LRPC Windows для большинства сообщений. Для управления рабочими процессами мы в значительной степени полагаемся на названные события, недавно я опубликовал библиотеку межпроцессных событий здесь.