Вопрос о том, как реализовать приложение хоста ac # с плагиноподобной архитектурой - PullRequest
6 голосов
/ 23 апреля 2010

Я хочу иметь приложение, которое работает в качестве хоста для многих других небольших приложений. Каждое из этих приложений должно работать как своего рода плагин для этого основного приложения. Я называю их плагинами не в том смысле, что они добавляют что-то в основное приложение, а потому, что они могут работать только с этим приложением Host, поскольку зависят от некоторых его служб.

Моя идея состояла в том, чтобы каждый из этих плагинов работал в другом домене приложения. Кажется, проблема в том, что у моего хост-приложения должен быть набор сервисов, которые мои плагины захотят использовать, и, насколько я понимаю, поток данных из разных доменов приложений не так уж и хорош.

С одной стороны, я бы хотел, чтобы они вели себя как автономные приложения (хотя, как я уже сказал, им нужно много раз использовать службы приложений хоста), но с другой стороны, мне бы это понравилось, если таковые имеются. из них происходит сбой, мое основное приложение не пострадает от этого.

Каков наилучший (.NET) подход к такого рода ситуации? Заставить их всех работать на одном домене приложений, но каждый в другой теме? Использовать разные домены приложений? По одному на каждый «плагин»? Как мне заставить их общаться с хост-приложением? Есть ли другой способ сделать это?

Хотя скорость здесь не является проблемой, я бы не хотел, чтобы вызовы функций были намного медленнее, чем при работе с обычным приложением .NET.

Спасибо

РЕДАКТИРОВАТЬ: Может быть, мне действительно нужно использовать разные домены приложений. Из того, что я читал, загрузка сборок в разных доменах приложений - единственный способ позже выгрузить их из процесса.

Ответы [ 3 ]

5 голосов
/ 23 апреля 2010

Я реализовал что-то в этом духе, используя Managed Addin Framework (MAF) в пространстве имен System.Addin. С MAF вы упаковываете свои надстройки как отдельные библиотеки DLL, которые ваше хост-приложение может обнаружить и запустить в своем домене приложений, в отдельном домене для всех надстроек или в каждом надстройке в своем собственном домене. С помощью теневого копирования и отдельных доменов вы даже можете обновить надстройку, не закрывая хоста.

Ваше хост-приложение и надстройки взаимодействуют через контракты, которые вы выводите из интерфейсов MAF. Вы можете отправлять объекты назад и вперед между хостом и надстройками. Cotnracts предоставляют интерфейс черного ящика между надстройками и хостом, позволяя вам изменять реализацию аддина без ведома хоста.

Надстройки могут даже общаться между собой, если хост рассказывает им друг о друге. В моем случае надстройка для ведения журналов используется другими пользователями. Это позволяет мне подключать разные логгеры, не касаясь других надстроек или хоста.

Для моего приложения надстройка использует простые супервизорные классы, которые при запуске рабочих классов в своих собственных потоках выполняют всю обработку. Рабочие ловят свои собственные исключения, которые они возвращают своему супервизору через методы обратного вызова. Супервайзеры могут перезапустить работников или принять другие меры. Хост контролирует супервизоров через командный контракт, который инструктирует их запускать и останавливать работников и возвращать данные.

Моим хост-приложением является служба Windows. Рабочие потоки выдавали исключения по всем обычным причинам (включая ошибки!), Но приложение хоста никогда не падало ни в одной из наших установок. Поскольку сервисы отладки неудобны, надстройки позволяют мне создавать тестовые приложения, использующие те же контракты, с дополнительной уверенностью, что я тестирую то, что развертываю.

Надстройки также могут предоставлять элементы пользовательского интерфейса. Это очень полезно для меня, так как мне нужно развернуть приложение контроллера с хост-сервисом, поскольку сервисы не имеют пользовательского интерфейса. Каждый плагин имеет свой собственный интерфейс контроллера. Само приложение контроллера очень просто - оно загружает надстройки и отображает их элементы пользовательского интерфейса. Это позволяет мне отправлять обновленный надстройку с обновленным интерфейсом и не нужно отправлять новый контроллер.

Несмотря на то, что контроллер и служба хоста используют одни и те же надстройки, они не наступают друг на друга; на самом деле, они даже не знают, что другое приложение использует те же надстройки. Контроллер и хост общаются друг с другом через общую базу данных, но вы также можете использовать другой механизм между приложениями, такой как MSMQ. В следующей версии хост будет службой WCF с надстройками на сервере и веб-службами для управления.

Это немного скучно, но я хотел дать вам представление о том, насколько универсален MAF. Это не так сложно, как может показаться на первый взгляд, и с его помощью можно создавать надежные приложения.

2 голосов
/ 23 апреля 2010

Это зависит от того, насколько вы доверяете расширениям. Я работаю над аналогичным приложением, и я решил в основном доверять коду расширения, поскольку это сильно упрощает вещи. Я вызываю код из общего потока (в моем случае расширения на самом деле не «запускаются» в каком-либо непрерывном цикле, а скорее выполняют определенные задачи, которые хочет выполнить основное приложение) и перехватывают исключения в этом потоке, чтобы предоставлять полезные предупреждения о том, что загруженные расширения работают неправильно.

В настоящее время ничто не мешает этим расширениям запускать свои собственные потоки, которые могут привести к сбою и аварийному завершению всего приложения, но здесь я должен был найти компромисс между безопасностью и сложностью. Мое приложение не является критически важным (не похоже на веб-сервер или сервер базы данных), поэтому я считаю приемлемым риск того, что неисправное расширение может привести к сбою приложения. Я предоставляю меры предосторожности для более вежливого освещения наиболее распространенных случаев сбоев и предоставляю разработчикам плагинов (которые в любом случае в основном будут штатными сотрудниками) для устранения ошибок.


Что касается выгрузки, да, вы можете выгрузить код и метаданные для сборки, только если поместите их в домен приложения. Тем не менее, если вы не хотите часто загружать и выгружать в течение всей жизни вашей программы, накладные расходы, связанные с хранением кода в памяти, не обязательно являются проблемой. Любые фактические экземпляры или ресурсы, использующие типы из сборки, будут по-прежнему очищаться GC, когда вы перестанете «использовать» его, поэтому тот факт, что он все еще находится в памяти, не означает утечку памяти.

Если ваш основной вариант использования представляет собой серию плагинов, которые вы находите один раз при запуске, а затем предоставляете возможность создания экземпляров во время работы вашего приложения, я предлагаю изучить реальный объем памяти, связанный с загрузкой всех из них при запуске и держать их загруженными. Если вы используете AppDomains, там также будут дополнительные издержки (например, память для прокси-объектов и загруженный / JITed-код для поддержки маршалинга AppDomain). Также будет происходить загрузка ЦП, связанная с маршалингом и сериализацией обслуживающего персонала.

Короче говоря, я бы использовал домены приложений только в том случае, если выполнялось одно из следующих действий:

  • Я хочу получить истинную изоляцию в целях обеспечения безопасности кода (т. Е. Мне нужно запускать ненадежный код изолированным способом)

  • Мое приложение является критически важным, и мне абсолютно необходимо убедиться, что в случае сбоя плагина он не сможет закрыть мое основное приложение.

  • Мне нужно многократно загружать и выгружать один и тот же плагин, чтобы поддерживать динамические изменения в DLL. Это в основном, если мое приложение не может перестать работать, но я хочу устанавливать исправления для плагинов, пока оно еще работает.

Я бы не предпочел AppDomains с единственной целью уменьшить возможный объем памяти, разрешив Unload.

1 голос
/ 23 апреля 2010

Это сложный вопрос.

Моя первая идея состояла в том, чтобы просто внедрить интерфейсы из вашего хост-приложения в ваши приложения-плагины, чтобы они могли обмениваться данными через Reflection, но это только позволило бы общаться и не принесло бынастоящая «песочница» архитектура.

Моя вторая мысль заключалась в разработке сервис-ориентированной платформы.Хост-приложение будет своего рода «плагином вещателя», который будет публиковать ваши плагины в ServiceHost в другом потоке.Так как это должно быть действительно отзывчивым и «не требующим дополнительной настройки», хост-приложение может связываться с плагином через канал именованных каналов (NetNamedPipesBinding для WCF), что означает, что он обменивается данными только с каналами localhost и не требует никакой конфигурации или знаний сети,Я думаю, что это может быть хорошим решением вашей проблемы.

С уважением.

...