Московский государственный технический университет им. Н. Э. Баумана
Поддержка разработки распределенных приложений в Microsoft .NET Framework
Учебный курс
Горин С. В., Крищенко В. А.
Москва, 2006
Содержание
Содержание Введение ....................................................................................................................................2 Тема 1.
Введение в распределенные системы.................................................................4
1.1.
Понятие распределенной системы ____________________________________ 4
1.2.
Определение распределенной системы. Программные компоненты ________ 9
1.3.
Требования к распределенным системам______________________________ 10
1.4.
Понятие промежуточной среды _____________________________________ 14
Тема 2.
Взаимодействие компонент распределенной системы...................................18
2.1.
Модели взаимодействия компонент распределенной системы ____________ 18
2.2.
Обмен сообщениями ______________________________________________ 19
2.3.
Дальний вызов процедур ___________________________________________ 21
2.4.
Использование удаленных объектов _________________________________ 23
Модель единственного вызова ......................................................................................26 Модель единственного экземпляра ..............................................................................27 Активация по запросу клиента......................................................................................28 Состояние компоненты распределенной системы ......................................................28 Использование свойств удаленных объектов ..............................................................29 2.5.
Распределенные события ___________________________________________ 32
2.6.
Распределенные транзакции ________________________________________ 33
2.7.
Безопасность в распределенных системах_____________________________ 35
2.8.
Промежуточные среды в Microsoft .NET Framework ____________________ 36
Тема 3.
Описание интерфейса программной компоненты ..........................................38
3.1.
Сервисы и интерфейс программной компоненты _______________________ 38
3.2.
Язык XML и схемы XML___________________________________________ 41
3.3.
SOAP: язык сообщений распределенной системы ______________________ 44
3.4.
WSDL: описание интерфейса программной компоненты ________________ 45
3.5.
Выводы по описанию интерфейса компоненты ________________________ 49 i
Содержание Тема 4.
Сериализация объектов. Способы сериализации в .NET Framework ............51
4.1.
Сериализация графа объектов _______________________________________ 51
4.2.
Методы сериализации в .NET Framework _____________________________ 54
4.3.
Класс сериализации XmlSerializer ___________________________________ 56
4.4.
Классы сериализации SoapFormatter и BinaryFormatter __________________ 67
4.5.
Выводы по использованию классов сериализации ______________________ 71
Тема 5.
Microsoft Message Queuing (MSMQ) – промежуточная среда обмена
сообщениями...........................................................................................................................73 5.1.
Служба обмена сообщениями MSMQ ________________________________ 73
5.2.
Инфраструктура, необходимая для использования MSMQ _______________ 75
5.3.
Применение службы сообщений MSMQ в распределенных системах______ 77
5.4.
Использование очередей сообщений MSMQ в .NET Framework __________ 80
5.5.
Выводы по использованию MSMQ __________________________________ 89
Тема 6.
Промежуточная среда COM+ и служба Enterprise Services ...........................91
6.1.
Введение в промежуточную среду COM+ _____________________________ 91
6.2.
Сервисы COM+___________________________________________________ 93
Синхронизация ...............................................................................................................93 Балансировка нагрузки ..................................................................................................94 Just-in-time-активация и пул объектов..........................................................................94 Распределенные транзакции..........................................................................................95 Ожидающие компоненты ..............................................................................................97 Слабо связанные события..............................................................................................98 Обеспечение безопасности ............................................................................................99 6.3.
Использование среды COM+ в приложениях .NET Framework____________ 99
Взаимодействие среды COM+ и среды CLR ...............................................................99 Создание обслуживаемых компонент ........................................................................101 Регистрация обслуживаемых компонент ...................................................................104 Использование исключений в обслуживаемых компонентах..................................106 Создание компенсирующего менеджера ресурсов ...................................................107 Использование слабо связанных событий .................................................................116 Сервисы COM+ без компонент COM+.......................................................................120 ii
Содержание 6.4. Тема 7.
Выводы по использованию среды Enterprise Services / COM+ ___________ 122 Веб-службы ASP.NET ......................................................................................125
7.1.
Введение в веб-службы ___________________________________________ 125
7.2.
Использование расширения WSE ___________________________________ 128
7.3.
Создание веб-службы в среде .NET Framework _______________________ 130
Описание веб-службы ..................................................................................................130 Создание посредника веб-службы ..............................................................................132 7.4.
Реализация нестандартного расширения WSE ________________________ 133
7.5.
Менеджер пользовательских записей________________________________ 138
7.6.
Выводы по использованию веб-служб _______________________________ 148
Тема 8.
Среда .NET Remoting .......................................................................................150
8.1.
Введение в среду .NET Remoting ___________________________________ 150
8.2.
Архитектура среды .NET Remoting _________________________________ 152
8.3.
Конфигурирование среды .NET Remoting ____________________________ 156
8.4.
Веб-службы в .NET Remoting ______________________________________ 160
8.5.
Канал среды Remoting ____________________________________________ 163
8.6.
Создание нестандартного канала ___________________________________ 170
8.7.
Выводы по использованию .NET Remoting ___________________________ 178
Тема 9.
Обеспечение безопасности распределенных систем в .NET Framework ....181
9.1.
Введение в обеспечение безопасности_______________________________ 181
9.2.
Механизмы обеспечения безопасности ______________________________ 183
Электронные сертификаты ..........................................................................................183 Протокол Kerberos........................................................................................................186 9.3.
Безопасность промежуточных сред .NET Framework___________________ 187
9.4.
Выводы по безопасности промежуточных сред _______________________ 189
Тема 10.
Применение промежуточных сред .................................................................190
10.1.
Взаимосвязь промежуточных сред __________________________________ 190
10.2.
Сравнение технологий создания распределенных систем ______________ 191
10.3.
Выводы по применению промежуточных сред .NET Framework _________ 194
iii
Содержание Список использованной литературы ..................................................................................195 Приложение I. Администрирование каталога COM+ .......................................................196 Приложение II. Использование ASP.NET без IIS..............................................................199 Приложение III. Симметричное шифрование....................................................................204
iv
Введение
Сведения об авторах Горин Сергей Викторович, доцент МГТУ им. Баумана, Москва,
[email protected]. Крищенко Всеволод Александрович, к.т.н., доцент МГТУ им. Баумана, Москва,
[email protected].
Системные требования к курсу 1. Microsoft .NET Framework 2.0 2. Для работы всех примеров требуется Microsoft Windows XP Pro SP2, или Microsoft Server 2003, или последующие операционные системы с установленными службами MSMQ, COM+ и IIS
Благодарности Курс создан при поддержке Microsoft Corporation в рамках программы SE Contest 2006.
Правовая информация Microsoft и Microsoft Windows являются зарезервированными торговыми марками или торговыми марками Microsoft Corporation, США. Прочие упомянутые в курсе названия продуктов и организаций могут быть торговыми марками их владельцев.
1
Введение
Введение В настоящее время много внимания уделяется технологиям разработки распределенных приложений, охватывающих несколько независимых компьютеров. В течение последних десяти лет было создано большое число технологий и стандартов, использование
которых
должно
было
помочь
разработчикам
в
создании
распределенных приложений масштаба предприятия. Однако поддержка многих технологий была изначально достаточно трудоемкой и сложной для разработчиков прикладных программ, использовавших классические языки программирования, такие как C/С++. Одной из задач, стоящих перед разработчиками Microsoft, создающими так называемую общеязыковую инфраструктуру (Common Language Infrastructure, CLI), так же известную как .NET, была наиболее полная поддержка средств разработки распределенных систем. Поэтому в платформе разработки приложений Microsoft .NET Framework имеется встроенная поддержка четырех взаимосвязанных технологий, предназначенных для использования в распределенных системах: очередей сообщений (messaging queues), объектов COM+, объектов .NET Remoting, веб-служб (web services). Каждая из данных технологий имеет свои достоинства, недостатки и особенности применения при построении распределенных приложений. Сделать осознанный выбор с пользу той или иной технологии при решения конкретных прикладных задач трудно без знакомства со всеми ними, также как и без базовых теоретических знаний о распределенных системах. Поэтому при написании данного учебного курса ставились следующие цели. 1. Ознакомить слушателей с общими вопросами создания распределенных систем, рекомендациями по архитектуре сложных приложений. 2. Дать представление о технологиях создания распределенных приложений, поддерживаемые платформой Microsoft .NET, показать их особенности и взаимосвязь. 3. Дать критерии выбора той или иной технологии при создании распределенных систем, показать границы их применимости. 2
Введение Курс рассчитан на студентов средних или старших курсов. Слушатели должны быть знакомы с архитектурой Microsoft.NET Framework, а так же иметь представление об и языке программирования C#, основных сетевых протоколах стека TCP/IP, основах криптографии, теории графов и формальных языков. Курс отражает точку зрения авторов на текущее (весна 2006 года) состояние технологии разработки .NET и связанных с нею технологий. При разработке курса ни в коем случае не ставилась цель подменить им техническую документацию.
3
Тема 1. Введение в распределенные системы
Тема 1. Введение в распределенные системы Дается представление о распределенных системах, компонентах распределенной системы. Формулируются требования к распределенным системам. Вводится понятие промежуточной среды.
1.1.
Понятие распределенной системы
По утверждению известного специалиста в области информатики Э. Таненбаума, не существует общепринятого и в то же время строгого определения распределенной системы. Некоторые остряки утверждают, что распределенной является такая вычислительная система, в которой неисправность компьютера, о существовании которого пользователи ранее даже не подозревали, приводит к остановке всей их работы. Значительная часть распределенных вычислительных систем, к сожалению, удовлетворяют такому определению, однако формально оно относится только к системам с уникальной точкой уязвимости (single point of failure). Часто при определении распределенной системы во главу угла ставят разделение ее функций между несколькими компьютерами. При таком подходе распределенной является любая вычислительная система, где обработка данных разделена между двумя и более компьютерами. Основываясь на определении Э. Таненбаума, несколько более узко распределенную систему можно определить как набор соединенных каналами связи независимых компьютеров, которые с точки зрения пользователя некоторого программного обеспечения выглядят единым целым. Такой подход к определению распределенной системы имеет свои недостатки. Например,
все
используемое
в
такой
распределенной
системе
программное
обеспечение могло бы работать и на одном единственном компьютере, однако с точки зрения приведенного выше определения такая система уже перестанет быть распределенной. Поэтому понятие распределенной системы, вероятно, должно основываться на анализе образующего такую систему программного обеспечения.
4
Тема 1. Введение в распределенные системы Как основу описания взаимодействия двух сущностей рассмотрим общую модель взаимодействия клиент-сервер, в которой одна сторон (клиент) инициирует обмен данными, посылая запрос другой стороне (серверу). Сервер обрабатывает запрос и при необходимости посылает ответ клиенту (рис. 1.1).
Запрос Клиент
Сервер Ответ
Рисунок 1.1. Модель взаимодействия клиент-сервер Взаимодействие в рамках модели клиент-сервер может быть как синхронным, когда клиент ожидает завершения обработки своего запроса сервером, так и асинхронным, при котором клиент посылает серверу запрос и продолжает свое выполнение без ожидания ответа сервера. Модель клиента и сервера может использоваться как основа описания различных взаимодействий. Для данного курса важно взаимодействие составных частей программного обеспечения, образующего распределенную систему. Пользователь Интерфейс пользователя Логика приложения Доступ к данным
База данных
Рисунок 1.2. Логические уровни приложения Рассмотрим некое типичное приложение, которое в соответствие с современными представлениями может быть разделено на следующие логические уровни (рис. 1.2): 5
Тема 1. Введение в распределенные системы пользовательский интерфейс (ИП), логика приложения (ЛП) и доступ к данным (ДД), работающий с базой данных (БД). Пользователь системы взаимодействует с ней через интерфейс пользователя, база данных хранит данные, описывающие предметную область приложения, а уровень логики приложения реализует все алгоритмы, относящиеся к предметной области. Поскольку на практике разных пользователей системы обычно интересует доступ к одним и тем же данным, наиболее простым разнесением функций такой системы между несколькими компьютерами будет разделение логических уровней приложения между одной серверной частью приложения, отвечающим за доступ к данным, и находящимися на нескольких компьютерах клиентскими частями, реализующими интерфейс пользователя. Логика приложения может быть отнесена к серверу, клиентам, или разделена между ними (рис. 1.3). Пользователь
Интерфейс пользователя Логика приложения
Клиентская составляющая системы
Логика приложения Доступ к данным
Серверная составляющая системы
База данных
Рисунок 1.3. Двухзвенная архитектура Архитектуру
построенных
по
такому
принципу
приложений
называют
клиент-серверной или двухзвенной. На практике подобные системы часто не относят к классу
распределенных,
но
формально
они
могут
считаться
простейшими
представителями распределенных систем. Развитием архитектуры клиент-сервер является трехзвенная архитектура, в которой интерфейс пользователя, логика приложения и доступ к данным выделены в 6
Тема 1. Введение в распределенные системы самостоятельные составляющие системы, которые могут работать на независимых компьютерах (рис. 1.4). Пользователь
Интерфейс пользователя
Клиентская часть приложения
Логика приложения
Сервер приложения
Доступ к данным
Сервер базы данных
База данных
Рисунок 1.4. Трехзвенная архитектура Запрос пользователя в подобных системах последовательно обрабатывается клиентской частью системы, сервером логики приложения и сервером баз данных. Однако обычно под распределенной системой понимают системы с более сложной архитектурой, чем трехзвенная. Пользователь
ИП ЛП ДД
ЛП ДД БД
БД
Система онлайн-платежей
Система розничной продажи
ЛП ДД БД Партер по поставкам товаров
Рисунок 1.5. Распределенная система розничных продаж
7
Тема 1. Введение в распределенные системы Применительно
к
приложениям
автоматизации
деятельности
предприятия,
распределенными обычно называют системы с логикой приложения, распределенной между несколькими компонентами системы, каждая из которых может выполняться на отдельном компьютере. Например, реализация логики приложения системы розничных продаж должна использовать запросы к логике приложения третьих фирм, таких как поставщики товаров, системы электронных платежей или банки, предоставляющие потребительские кредиты (рис 1.5). Таким образом, в обиходе под распределенной системой часто подразумевают рост многозвенной архитектуры «в ширину», когда запросы пользователя не проходят последовательно от интерфейса пользователя до единственного сервера баз данных. В качестве другого примера распределенной системы можно привести сети прямого обмена данными между клиентами (peer-to-peer networks). Если предыдущий пример имел «древовидную» архитектуру, то сети прямого обмена организованы более сложным образом, рис. 1.6. вероятно,
одними
из
Подобные системы являются в настоящий момент,
крупнейших
существующих
распределенных
систем,
объединяющие миллионы компьютеров. Управляющий сервер
Управляющий сервер
ЛП
ЛП ДД
Данные
ЛП
ЛП ИП
Клиент
Пользователь
ЛП ДД
Данные
ЛП ИП
Клиент
Пользователь
Рисунок 1.6. Система прямого обмена данными между клиентами
8
Тема 1. Введение в распределенные системы 1.2.
Определение распределенной системы. Программные компоненты
В распределенных системах функции одного уровня приложения могут быть разнесены между несколькими компьютерами. С другой стороны, программное обеспечение, установленное на одном компьютере, может отвечать за выполнение функций, относящихся к разным уровням. Поэтому подход к определению распределенной системы, считающей ее совокупностью компьютеров, условен. Для описания и реализации распределенных систем было введено понятие программной компоненты. Программная компонента – это единица программного обеспечения, исполняемая на одном компьютере в пределах одного процесса, и предоставляющая некоторый набор сервисов, которые используются через ее внешний интерфейс другими компонентами, как выполняющимися на этом же компьютере, так и на удаленных компьютерах. Ряд компонент пользовательского интерфейса предоставляют свой сервис конечному пользователю.
Пользователи
Пользователи
Компоненты интерфейса пользователя Компоненты логики приложения Компоненты доступа к данным
Источник данных
Источник данных
Рисунок 1.7. Компоненты распределенной системы 9
Тема 1. Введение в распределенные системы Применительно к программам с использованием платформы CLI, под процессом в приведенном
определении
компоненты
следует
понимать
домен
приложения
(application domain), который можно рассматривать как аналог процесса в управляемом коде. Основываясь на определении программной компоненты, можно дать более точное определение распределенной системы. Согласно нему, распределенная система есть набор взаимодействующих программных компонент, выполняющихся на одном или нескольких связанных компьютерах и выглядящих с точки зрения пользователя системы как единое целое (рис. 1.7). Прозрачность является атрибутом распределенной системы. При исправном функционировании системы от конечного пользователя должно быть скрыто, где и как выполняются его запросы. Программная
компонента
является
минимальной
единицей
развертывания
распределенной системы. В ходе модернизации системы одни компоненты могут быть обновлены независимо от прочих компонент. В хорошо спроектированной системе функции каждой компоненты относятся только к одному уровню приложения. Однако разделение только на три уровня представляется недостаточным
для
классификации
компонент.
Например,
часть
компонент
пользовательского интерфейса могут взаимодействовать с пользователем, а часть – предоставлять
свои
сервисы
другим
компонентам,
но
с
пользователем
не
взаимодействовать. Классификации подобного рода существуют, однако они не являются общепринятыми и часто в значительной степени привязаны к приложениям автоматизации деятельности предприятия, что все-таки не является синонимом распределенной системы. 1.3.
Требования к распределенным системам
Чтобы достигнуть цели своего существования – улучшения выполнения запросов пользователя
–
распределенная
система
должна
удовлетворять
некоторым
необходимым требованиям. Можно сформулировать следующий набор требований, которым в наилучшем случае должна удовлетворять распределенная вычислительная система. 10
Тема 1. Введение в распределенные системы Открытость. Все протоколы взаимодействия компонент внутри распределенной системы в идеальном случае должны быть основаны на общедоступных стандартах. Это позволяет использовать для создания компонент различные средства разработки и операционные системы. Каждая компонента должна иметь точную и полную спецификацию своих сервисов. В этом случае компоненты распределенной системы могут быть созданы независимыми разработчиками. При нарушении этого требования может исчезнуть возможность создания распределенной системы, охватывающей несколько независимых организаций. Масштабируемость. Масштабируемость вычислительных систем имеет несколько аспектов. Наиболее важный из них для данного курса – возможность добавление в распределенную систему новых компьютеров для увеличения производительности системы, что связано с понятием балансировки нагрузки (load balancing) на серверы системы. К масштабированию относятся так же вопросы эффективного распределение ресурсов сервера, обслуживающего запросы клиентов. Поддержание
логической
целостности
данных.
Запрос
пользователя
в
распределенной системе должен либо корректно выполняться целиком, либо не выполняться вообще. Ситуация, когда часть компонент системы корректно обработали поступивший запрос, а часть – нет, является наихудшей. Устойчивость.
Под
устойчивостью
понимается
возможность
дублирования
несколькими компьютерами одних и тех же функций или же возможность автоматического распределения функций внутри системы в случае выхода из строя одного из компьютеров. В идеальном случае это означает полное отсутствие уникальной точки сбоя, то есть выход из строя одного любого компьютера не приводит к невозможности обслужить запрос пользователя. Безопасность. Каждый компонент, образующий распределенную систему, должен быть уверен, что его функции используются авторизированными на это компонентами или пользователями. Данные, передаваемые между компонентами, должны быть защищены как от искажения, так и от просмотра третьими сторонами.
11
Тема 1. Введение в распределенные системы Эффективность. В узком смысле применительно к распределенным системам под эффективностью будет пониматься минимизация накладных расходов, связанных с распределенным характером системы. Поскольку эффективность в данном узком смысле может противоречить безопасности, открытости и надежности системы, следует отметить, что требование эффективности в данном контексте является наименее приоритетным. Например, на поддержку логической целостности данных в распределенной системе могут тратиться значительные ресурсы времени и памяти, однако система с недостоверными данными вряд ли нужна пользователям. Желательным свойством промежуточной среды является возможность организации эффективного обмена данными, если взаимодействующие программные компоненты находятся на одного компьютере. Эффективная промежуточная среда должна иметь возможность организации их взаимодействия без затрагивания стека TCP/IP. Для этого могут использоваться системные сокеты (unix sockets) в POSIX-системах или именованные каналы (named pipes). Устойчивость распределенной системы связана с понятием масштабируемости, но не эквивалентна ему. Допустим, система использует набор обрабатывающих запросы серверов и один диспетчер запросов, который распределяет запросы пользователей между серверами. Такая система может считаться достаточно хорошо масштабируемой, однако диспетчер является уязвимой точкой такой системы. С другой стороны, система с единственным сервером может быть устойчива, если существует механизм его автоматической замены в случае выхода его из строя, однако она вряд ли относится к классу хорошо масштабируемых систем. На практике достаточно часто встречаются распределенные системы, не удовлетворяющие данным требованиям: например, любая система с уникальным сервером БД, реализованным в виде единственного компьютера, имеет
уникальную
точку
сбоя.
Выполнение
требований
устойчивости
и
масштабируемости обычно связано с некоторыми дополнительным расходами, что на практике может быть не всегда целесообразно. Однако используемые при построении распределенных систем технологии должны допускать принципиальную возможность создания устойчивых и высоко масштабируемых систем. Классическим
примером
системы,
в
значительной
мере
отвечающей
всем
представленным выше требованиям, является система преобразования символьных 12
Тема 1. Введение в распределенные системы имен в сетевые IP-адреса (DNS). Система имен – организованная иерархически распределенная система, с дублированием всех функций между двумя и более серверами (рис. 1.8). Пользователь
Сервер распознавания имен
ИП ЛП Клиентское приложение
Иерархия DNS-серверов ЛП ДД
ЛП
ДД
ЛП ДД
БД ЛП ДД
БД
БД
Корневой сервер
Сервер зоны
Сервер, отвественный за домейн БД
Рисунок 1.8. Система DNS Запрос пользователя на преобразование имени (например, w3c.org) в сетевой адрес передается серверу распознавания имен поставщика услуг интернета. Сервер распознавания имен по очереди опрашивает серверы из иерархии службы имен. Опрос начинается с корневых серверов, который возвращает адреса серверов, ответственных за зону домена. Затем опрашивается сервер, ответственный за зону (в данном случае – .org), возвращающий адреса серверов, ответственных за домен второго уровня, и так далее. Серверы имен кешируют информацию о соответствии имен и адресов для уменьшения нагрузки на систему. Программное обеспечение на компьютере пользователя обычно имеет возможность соединиться с как минимум двумя различными серверами распознавания имен. Тем не менее, и в системе распознавания имен не все требования к распределенным системам выполнены. В частности, она не содержит каких-либо явных механизмов
13
Тема 1. Введение в распределенные системы обеспечения безопасности. Это приводит к регулярным атакам на серверы имен в надежде вывести их из строя, например, большим количеством запросов. 1.4.
Понятие промежуточной среды
С точки зрения одного из компьютеров распределенной системы, все другие входящие в нее машины являются удаленными вычислительными системами. Теоретической основой сетевого взаимодействия удаленных систем является общеизвестная модель взаимодействия открытых систем OSI/ISO, которая разделяет процесс взаимодействия двух сторон на семь уровней: физический, канальный, сетевой, транспортный, сеансовый, прикладной, представительский. Система 1
Система 2
Прикладной
Прикладной
Представительский
Представительский
Сеансовый
Сеансовый
Транпортный
Транпортный
Сетевой
Сетевой
Канальный
Канальный
Физический
Канал связи
Программные компоненты Промежуточная среда
Операционная система
Физический
Рисунок 1.9. Модель взаимодействия вычислительных систем В сетях наиболее распространенного стека протоколов TCP/IP протокол TCP является протоколом транспортного, а протокол IP – протоколом сетевого уровня. Обеспечение интерфейса к транспортному уровню в настоящее время берет на себя сетевая компонента операционной системы, предоставляя обычно основанный на сокетах 14
Тема 1. Введение в распределенные системы интерфейс для верхних уровней. Сокеты обеспечивают примитивы низкого уровня для непосредственного обмена потоком байт между двумя процессами.
Стандартного
представительского или сеансового уровня в стеке протоколов TCP/IP нет, иногда к ним относят защищенные протоколы SSL/TLS. Использование протокола TCP/IP посредством сокетов предоставляет стандартный, межплатформенный, но низкоуровневый сервис для обмена данными между компонентами.
Для
выполнения
сформулированных
выше
требований
к
распределенным системам функции сеансового и представительского уровня должна взять на себя некоторая промежуточная среда (middleware), называемая так же промежуточным программным обеспечением (рис. 1.9). Такая среда должна помочь создать разработчиками открытые, масштабируемые и устойчивые распределенные системы. Для достижения этой цели промежуточная среда должна обеспечить сервисы для взаимодействия компонент распределенной системы. К таким сервисам относятся: –
обеспечение единого и независимого от операционной системы механизма использования
одними
программными
компонентами
сервисов
других
компонент; –
обеспечение
безопасности
распределенной
системы:
аутентификация
и
авторизация всех пользователей сервисов компоненты и защита передаваемой между компонентами информации от искажения и чтения третьими сторонами; –
обеспечение целостности данных: управление транзакциями, распределенными между удаленными компонентами системами;
–
балансировка нагрузки на серверы с программными компонентами;
–
обнаружение удаленных компонент.
В пределах одной распределенной системы может использоваться несколько видов промежуточных сред (рис. 1.10). При хорошем подходе к проектированию системы каждая распределенная ее компонента предоставляет свои сервисы посредством единственной промежуточной среды, и использует сервисы других компонент посредством так же единственной промежуточной среды, однако эти среды могут быть различными.
15
Тема 1. Введение в распределенные системы Пользователи
Пользователи
ИП
ИП Промежуточная среда 2
Промежуточная ИП среда 1 Промежуточная среда 1
ИП
ЛП Промежуточная среда 3 Промежуточная среда 4 БД
ЛП
ЛП
ДД
ЛП
ДД
БД
Рисунок 1.10. Гетерогенная распределенная система Распределенную систему, компоненты которой используют несколько промежуточных сред, можно называть гетерогенной, в противоположность гомогенной, использующей единственную промежуточную среду. Поскольку одна и та же промежуточная среда может быть реализована на различных аппаратных платформах и операционных системах, то оба класса распределенных систем могут включать в себя компьютеры под управлением как одной, так и различных операционных систем. В настоящий момент нет универсально применимой промежуточной среды, хотя как будет показано в курсе, есть определенное движение в этом направлении. Основной причиной отсутствия такой среды являются отчасти противоречивые требования к распределенным системам, а так же различающийся характер сетевых соединений между компонентами системы: например взаимодействие компонент внутри одного предприятия, вероятно, может быть построено иначе, чем взаимодействие компонент двух различных предприятий, не полностью доверяющих друг другу. Взаимодействие программных компонент в пределах одного и того же компьютера также происходит при помощи промежуточной среды, что при использовании некоторых промежуточных сред может быть как неудобно, так и неэффективно. В 16
Тема 1. Введение в распределенные системы идеальном случае распределенная компонента должна быть реализована таким образом, чтобы переход с одной промежуточной среды на другую происходил путем изменения конфигурации программной компоненты, а не изменения исходного кода. На практике данное требование, к сожалению, может быть трудно осуществимо, однако необходимо хотя бы минимизировать возможные исправления программного кода при возможной смене промежуточной среды.
17
Тема 3. Описание интерфейса программной компоненты
Тема 2. Взаимодействие компонент распределенной системы Рассматриваются
две
модели
взаимодействия
распределенных
компонент:
использование сообщений и удаленный вызов. Описываются различные подходы использования удаленных объектов и удаленных событий. Рассматривается понятие распределенной транзакции.
2.1.
Модели взаимодействия компонент распределенной системы
Ключевым сервисом промежуточной среды для создания распределенных систем является обеспечение обмена данными между компонентами распределенной системы. В настоящий момент существуют две концепции взаимодействия программных компонент: обмен сообщениями между компонентами и вызов процедур или методов объекта удаленной компоненты по аналогии с локальным вызовом процедуры. Поскольку
в
настоящее
время
любое
взаимодействие
между
удаленными
компонентами в конечном итоге основано на сокетах TCP/IP, первичным с точки зрения промежуточной среды является низкоуровневый обмен сообщениями на основе сетевых сокетов, сервис которых никак не определяет формат передаваемого сообщения. На базе протоколов TCP или HTTP затем могут быть построены прикладные протоколы обмена сообщений более высокого уровня абстракции для реализации более сложного обмена сообщениями или удаленного вызова процедур. Удаленный вызов является моделью, происходящей от языков программирования высокого уровня, а не от реализации интерфейса транспортного уровня сетевых протоколов. Поэтому протоколы удаленного вызова должны обязательно базироваться на какой-либо системе передачи сообщений, включая как непосредственное использование сокетов TCP/IP, так и основанные на нем другие промежуточные среды для обмена сообщениями. Реализация высокоуровневых служб обмена сообщениями, в свою очередь, может использовать удаленный вызов процедур, основанный на более низкоуровневой передаче сообщений, использующей, например, непосредственно сетевые сокеты. Таким образом, одна промежуточная среда может использовать для своего функционирования сервисы другой промежуточной среды, аналогично тому, как 18
Тема 3. Описание интерфейса программной компоненты один протокол транспортного или сетевого уровня может работать поверх другого протокола при туннелировании протоколов. 2.2.
Обмен сообщениями
Существует два метода передачи сообщений от одной удаленной системы к другой – непосредственный обмен сообщениями и использование очередей сообщений. В первом случае передача происходит напрямую, и она возможна только в том случае, если принимающая сторона готова принять сообщение в этот же момент времени. Во втором случае используется посредник – менеджер очередей сообщений. Компонента посылает сообщение в одну из очередей менеджера, после чего она может продолжить свою работу. В дальнейшем получающая сторона извлечет сообщение из очереди менеджера и приступит к его обработке. Простейшей
реализацией
непосредственного
обмена
сообщениями
является
использование транспортного уровня сети через интерфейс сокетов, минуя какое-либо промежуточное программное обеспечение. Однако такой способ взаимодействия обычно не применяется в системах автоматизации предприятия, поскольку в этом случае реализация всех функций промежуточной среды ложится на разработчиков приложения. При таком подходе сложно получить расширяемую и надежную распределенную систему, поэтому для разработки прикладных распределенных систем обычно используются системы очередей сообщений. Существует
несколько
разработок
в
области
промежуточного
программного
обеспечения, реализующие высокоуровневые сервисы для обмена сообщениями между программными компонентами. К ним относятся, в частности, Microsoft Message Queuing, IBM MQSeries и Sun Java System Message Queue. Такие системы дают возможность
приложениям
использовать
следующие
базовые
примитивы
по
использованию очередей: –
добавить сообщение в очередь;
–
взять первое сообщение из очереди, процесс блокируется до появления в очереди хотя бы одного сообщения;
–
проверить очередь на наличие сообщений; 19
Тема 3. Описание интерфейса программной компоненты –
установить обработчик, вызываемый при появлении сообщений в очереди.
Менеджер очереди сообщений в таких системах может находиться на компьютере, отличном от компьютеров с участвующими в обмене компонентами. В этом случае сообщение первоначально помещается в исходящую очередь на компьютере с посылающей сообщения компонентой, а затем пересылается менеджеру требуемой. Для
создания
крупных
систем
обмена
сообщениями
может
использоваться
маршрутизация сообщений, при которой сообщения не передаются напрямую менеджеру, поддерживающему очередь, а проходят через ряд промежуточных менеджеров очередей сообщений (рис. 2.1). Клиент
Менеджеры очередей сообщений
Приложение Промежуточная среда Исходящая очередь
Сервер Приложение
Промежуточная среда Очередь
Очередь
Промежуточная среда
Рисунок 2.1. Системы очередей сообщений Использование очередей сообщений ориентировано на асинхронный обмен данными. Основные достоинства таких систем: –
время функционирования сервера может быть не связано со временем работы клиентов;
–
независимость промежуточной среды от средства разработки компонент и используемого языка программирования;
–
считывать и обрабатывать заявки из очереди могут несколько независимых компонент, что дает возможность достаточно просто создавать устойчивые и масштабируемые системы.
Недостатки систем очередей сообщений являются продолжением их достоинств: –
необходимость явного использования очередей распределенным приложением; 20
Тема 3. Описание интерфейса программной компоненты –
сложность реализации синхронного обмена;
–
определенные накладные расходы на использование менеджеров очередей;
–
сложность получение ответа: передача ответа может потребовать отдельной очереди на каждый компонент, посылающий заявки.
2.3.
Дальний вызов процедур
Идея удаленного вызова процедур (remote procedure call, RPC) появилась в середине 80-х годов и заключалась в том, что при помощи промежуточного программного обеспечения функцию на удаленном компьютере можно вызывать так же, как и функцию на локальном компьютере. Чтобы удаленный вызов происходил прозрачно с точки зрения вызывающего приложения, промежуточная среда должна предоставить процедуру-заглушку (stub), которая будет вызываться клиентским приложением. После вызова процедуры-заглушки промежуточная среда преобразует переданные ей аргументы в вид, пригодный для передачи по транспортному протоколу, и передает их на удаленный компьютер с вызываемой функцией. На удаленном компьютере параметры извлекаются промежуточной средой из сообщения транспортного уровня и передаются вызываемой функции (рис. 2.2). Аналогичным образом на клиентскую машину передается результат выполнения вызванной функции. Клиент
Сервер
Клиентский процесс
Серверный процесс
Вызывающая функция
Вызываемая функция
Заглушка клиента
Заглушка сервера
Промежуточная среда
Промежуточная среда
Сетевые служба ОС
Сетевые служба ОС Канал передачи данных
Рисунок 2.2. Удаленный вызов процедур 21
Тема 3. Описание интерфейса программной компоненты Существует три возможных варианта удаленного вызова процедур. 1. Синхронный вызов: клиент ожидает завершения процедуры сервером и при необходимости получает от него результат выполнения удаленной функции; 2. Однонаправленный асинхронный вызов: клиент продолжает свое выполнение, получение ответа от сервера либо отсутствует, либо его реализация возложена на разработчика (например, через функцию клиента, удалено вызываемую сервером). Асинхронный вызов: клиент продолжает свое выполнение, при завершении сервером выполнения процедуры он получает уведомление и результат ее выполнения, например через
callback-функцию,
вызываемую
промежуточной
средой
при
получении
результата от сервера. Процесс преобразования параметров для передачи их между процессами (или доменами приложения в случае использования .NET) при удаленном вызове называется маршализацией (marshaling). Преобразование экземпляра какого-либо типа данных в пригодный для передачи за границы вызывающего процесса набор байт называется сериализацией. Десериализация – процедура, обратная сериализации – заключается в создании копии сериализованного объекта на основе полученного набора байт. Такой подход к передаче объекта между процессами путем создания его копий называется маршализацией по значению (marshal by value), в отличие от рассматриваемой в следующем разделе маршализации по ссылке. Процесс сериализации должен быть определен для всех типов данных, передаваемых при удаленном вызове. К ним относятся параметры вызываемой функции и возвращаемый функцией результат. В случае передачи параметров по ссылке сериализации подлежат ссылаемые объекты, поскольку для самих указателей сериализация не может быть применена. Это затрудняет использование механизма удаленного вызова в языках, поддерживающих указатели на объекты неизвестного типа.
22
Тема 3. Описание интерфейса программной компоненты 2.4.
Использование удаленных объектов
В связи с переходом разработчиков прикладных программ от структурной парадигмы к объектной появилась необходимость в использовании удаленных объектов (remote method invocation, RMI). Удаленный объект представляет собой некоторые данные, совокупность которых определяет его состояние. Это состояние можно изменять путем вызова его методов. Обычно возможен прямой доступ к данным удаленного объекта, при этом происходит неявный удаленный вызов, необходимый для передачи значения поля данных объекта между процессами. Методы и поля объекта, которые могут использоваться через удаленные вызовы, доступны через некоторый внешний интерфейс класса объекта. Внешний интерфейс компоненты распределенной системы в таких системах обычно совпадает с внешним интерфейсом одного из входящих в компоненту классов. В момент, когда клиент начинает использовать удаленный объект, на стороне клиента создается клиентская заглушка, называемая посредником (proxy). Посредник реализует тот же интерфейс, что и удаленный объект. Вызывающий процесс использует методы посредника, который маршализирует их параметры для передачи по сети, и передает их по сети серверу. Промежуточная среда на стороне сервера десериализует параметры и передает их заглушке на стороне сервера, которую называют каркасом (skeleton) или, как и в удаленном вызове процедур, заглушкой (рис. 2.3). Каркас связывается с некоторым экземпляром удаленного объекта. Это может быть как вновь созданный, так и существующий экземпляр объекта, в зависимости от применяемой модели использования удаленных объектов, которые будут рассмотрены ниже. Весь описанный процесс называется маршализацией удаленного объекта по ссылке (marshal by reference). В отличие от маршализации по значению, экземпляр объекта находится в процессе сервера и не покидает его, а для доступа к объекту клиенты используют посредников. При маршализации же по значению само значение объекта сериализуется в набор байт для его передачи между процессами, после чего следует создание его копии в другом процессе.
23
Тема 3. Описание интерфейса программной компоненты Клиент
Сервер
Клиентский процесс
Серверный процесс Объект Состояние объекта
Ccылка на посредника
Интерфейс удаленного объекта
Интерфейс удаленного объекта Посредник
Каркас
Промежуточная среда
Промежуточная среда
Сетевые служба ОС
Сетевые служба ОС Канал передачи данных
Рисунок 2.3. Использование удаленных объектов Как и удаленный вызов процедур, вызов метода удаленного объекта может быть как синхронным, так и асинхронным. Существует две особенности при использовании удаленных объектов, не встречавшихся в удаленном вызове процедур. Во-первых, если на момент формирования концепции удаленного вызова процедур исключения (exceptions) еще не поддерживались распространенными языками и не использовались, то в дальнейшем они стали методом информирования вызывающей стороны о встреченных
вызываемой
стороной
проблемах.
Таким
образом,
в
системах,
использующих удаленные объекты, сериализации подвержены как параметры метода и его результат, так и выбрасываемые при выполнении удаленного метода исключения. Во-вторых, в качестве параметра или результата методов могут тоже передаваться ссылки на удаленный объект (рис 2.4), если вызывающий удаленный метод клиент также может является сервером RMI.
24
Тема 3. Описание интерфейса программной компоненты Клиентский процесс
Серверный процесс
Маршализируемый по ссылке объект
Маршализируемый по ссылке объект
ссылка
ссылка
Каркас
Посредник
Посредник
Каркас
Промежуточная среда
Рисунок 2.4. Передача удаленному методу ссылки на объект, маршализируемый по ссылке При использовании удаленных объектов проблемными являются вопросы о времени их жизни: –
в какой момент времени создается экземпляр удаленного объекта;
–
в течение какого промежутка времени он существует.
Для описания жизненного цикла в системах с удаленными объектами используются два дополнительных понятия: –
активация объекта: процесс перевода созданного объекта в состояние обслуживания удаленного вызова, то есть связывания его с каркасом
и
посредником. –
деактивация объекта: процесс перевода объекта в неиспользуемое состояние.
Выделяют три модели использования удаленных объектов – модель единственного вызова (singlecall), модель единственного экземпляра (singleton), а так же модель активации объектов по запросу клиента (client activation). Первых две модели так же иногда называют моделями серверной активации (server activation), хотя, строго говоря, активация всегда происходит на сервере после какого-либо запроса от клиента.
25
Тема 3. Описание интерфейса программной компоненты Модель единственного вызова При использовании данной модели объект активируется на время единственного удаленного вызова. В наиболее простом случае для каждого вызова удаленного метода объекта клиентом на сервере создается и активируется новый экземпляр объекта, который деактивируется и затем удаляется сразу после завершения удаленного вызова метода объекта. Таким образом, удаленные вызовы разных клиентов изолированы друг от друга. Благодаря удалению объектов после вызова достигается экономное расходование ресурсов памяти, но могут тратиться значительные ресурсы процессора на постоянное создание и удаление объектов. Посредник на клиенте и заглушка на сервер существуют до уничтожения посредника объекта (рис. 2.5). Сервер
Клиентский процесс Вызов метода
Вызов метода
Промежуточная среда
Объект
Объект Вызов метода
Серверный процесс
Вызов метода
Клиент
Промежуточная среда
Рисунок 2.5. Режим единственного вызова удаленного метода Данный метод использования удаленных объектов можно рассматривать как некоторый вариант удаленного вызова процедур, поскольку объект не сохраняет свое состояние
между вызовами. Тем не менее, сервер использует свои ресурсы на
поддержание каркаса и канала между посредником и заглушкой. Определенным недостатком метода одного вызова является частое создание и удаление экземпляров объектов, поэтому в промежуточным средах может существовать сервис, позволяющий поддерживать некоторое количество уже созданных, но еще не 26
Тема 3. Описание интерфейса программной компоненты активированных объектов, которые используются для обработки удаленных вызовов. Такой набор объектов, ожидающих своей активации, называется пулом объектов (object pooling). По завершении удаленного вызова объекты деактивируются и могут либо быть помещены в пул и использованы повторно в дальнейшем, либо удаляются, если размер пула достиг некоторого максимального значения. Такая технология позволяет достичь баланса между скоростью обработки запроса и объемом используемых ресурсов сервера. Как видно из описания, в системах с пулом объектов активация не всегда следует непосредственно после создания объекта, а удаление не всегда следует сразу за деактивацией. Отличительной особенностью метода одного вызова являются наименьшие затраты на организацию системы балансировки нагрузки и ее наибольшая эффективность, поскольку каждый обслуживающий запросы сервер может обработать вызов любого удаленного метода. Модель единственного экземпляра При использовании модели единственного экземпляра удаленный объект существует не более чем в одном экземпляре. Созданный объект существует, пока есть хоть один использующий его клиент (рис. 2.6). Сервер
Клиентский процесс
Серверный процесс Объект
Клиентский процесс Вызов метода
Вызов метода
Вызов метода
Промежуточная среда
Клиент
Вызовы методов
Клиент
Промежуточная среда
Промежуточная среда
Рисунок 2.6. Использование удаленных объектов в режиме единственного экземпляра. 27
Тема 3. Описание интерфейса программной компоненты При использовании модели единственного объекта вызовы различных клиентов работают с одним и тем же экземпляром удаленного объекта. Поскольку вызовы клиентов не изолированы друг от друга, то используемый объект не должен иметь какого-либо внутреннего состояния. Модель единственного объекта позволяет получить наиболее высокую производительность, поскольку объекты не создаются и не активируются сервером при каждом вызове метода объекта. Активация по запросу клиента При каждом создании клиентом ссылки на удаленный объект (точнее, на посредника) на сервере создается новый объект, который существует, пока клиент не удалит ссылку на посредника. При таком методе использования вызовы различных клиентов изолированы друг от друга, и каждый объект сохраняет свое состояние между вызовами, что приводит к наименее рациональному использованию ресурсов памяти сервера (рис. 2.7). Сервер
Клиент
Объект
Ссылка
Ссылка
Объект
Ссылка
Клиентский процесс
Ссылка
Серверный процесс
Промежуточная среда
Промежуточная среда
Рисунок 2.7. Объекты, активируемые клиентом. Состояние компоненты распределенной системы Программные компоненты с точки зрения пользователей своих сервисов можно разделить на две категории: –
компоненты без сохраняемого между удаленными вызовами своих методов внутреннего состояния (stateless components);
28
Тема 3. Описание интерфейса программной компоненты –
компоненты с внутренним состоянием, сохраняемым между удаленными вызовами своих методов (statefull components).
Под состоянием в данном случае понимается совокупность значений полей реализующих компоненту объектов, хранящихся в памяти сервера. Если компонента в ходе своей работы сохраняет какие-либо данные во внешнем хранилище, например в базе данных или очереди сообщений, это обычно не рассматривается как ее внутреннее состояние. Модель единственного вызова не сохраняет состояния удаленного объекта между вызовами его методов, в силу чего данная модель может использоваться только с распределенными
компонентами
без
внутреннего
состояния.
Модель
одного
экземпляра может быть использована для вызова компонент с внутренним состоянием, но это вряд ли часто имеет смысл, поскольку ее состояние будет меняться каждым из клиентов в произвольном порядке. Модель активации по запросу клиента может быть использована с любыми компонентами, но для компонент без внутреннего состояния такой подход обычно ведет к непроизводительному расходу памяти при некотором выигрыше в затратах времени процессора по сравнению с моделью одного вызова. Компоненты без сохранения внутреннего состояния, используемые вместе с моделью единственного
вызова
с
пулом
объектов,
имеют
наибольшие
возможности
масштабирования системы при оптимальном балансе между затратами памяти и нагрузкой на процессор. Использование свойств удаленных объектов Рассмотрим следующий фрагмент кода, заполняющий свойства удаленного объекта информацией о человеке и вызывающий некий удаленный метод для ее обработки. processing.FirstName = "Иван"; processing.SecondName = "Иванов"; processing.ThirdName = "Иванович"; processing.City = "Москва"; result = processing.Run();
29
Тема 3. Описание интерфейса программной компоненты Если processing – локальный объект, то в методе Run() он будет иметь доступ к установленным здесь значениям своих методов. Предположим, что processing – удаленный объект. В этом случае возможны, например, следующие варианты. 1. Используется модель единственного вызова, причем установка свойства объекта рассматривается как вызов метода установки свойства (set в C#). В этом случае к моменту вызова метода Run состояние полей объекта не задано (при отсутствии пула объектов) или неопределенно (при использовании пула объектов). 2. Используется модель единственного вызова, но установка свойств объекта не рассматривается как удаленный вызов. Например, их значения сохраняются на стороне клиента и передаются на сервер в момент вызова метода, где на основе этих значений заполняются поля объекта. В этом случае результат будет корректен. 3. Используется модель активации по запросу клиента, или такой вариант модели единственного вызова, в котором присваивание свойств объекта приводит к передаче данных на сервер без последующей деактивации объекта (по существу такая модель уже не является моделью одного вызова). В этом случае результат так же будет корректен, но присваивание свойств объекту приводит к непроизводительным удаленным вызовам, 4. Используется модель одного экземпляра. В этом случае результат будет некорректным, поскольку поля объекта будут заполняться параллельно разными клиентами. Таким образом, один и тот же код может давать разные результаты в зависимости от модели использования удаленной компоненты, причем только в одном из примеров был получен корректный результат при минимальных накладных расходах. Можно сделать заключение, что использование в программе свойств удаленного объекта приводит к зависимости ее результата от используемой промежуточной среды и ее конфигурации. Наилучшим вариантом решения этой проблемы представляется отсутствие публичных свойств у удаленного объекта. В таком случае использование удаленного объекта может не отличаться от вызова метода локального объекта. Однако 30
Тема 3. Описание интерфейса программной компоненты применение такого подхода «в лоб» (так называемый chunky design) может привести к плохо читаемому коду, например следующему. result = processing.Run("Иванов", "Иван", "Иванович", "Москва");
В отличие от предыдущего фрагмента, это код не является самодокументирующимся, и увеличивает вероятность ошибки в последовательности аргументов вызываемого метода. В данном примере так же наблюдается смешивание двух сущностей: объекта, содержащего данные о человеке, и объекта, производящего с ним операции. Правильный подход заключается в создании маршализируемого по значению класса, содержащего все параметры удаленного вызова, и передача его экземпляра как параметра удаленного вызова метода. person.FirstName = "Иван"; person.SecondName = "Иванов"; person.ThirdName = "Иванович"; person.City = "Москва"; result = processing.Run(person);
Для многих задач представляет интерес вопрос модификации поведения удаленного объекта путем добавления некоторого дополнительного кода. Рассмотрим следующий фрагмент кода, вызывающий некий математический метод для заданной функции и заданного отрезка значений аргумента. Было бы удобно отделить реализацию самого математического метода от применяемой функции. task.Epsilon = 1e-5: task.X1 = -1; task.X2 = 1; task.MaximumSteps = 20; task.Function += X2Function; result = processing.Run(processingArguments); ... double X2Function(double x) { return x*x; }
Если processing – это удаленный объект, то данный код, вероятно, приведет к многочисленным удаленным вызовам функции X2Function. В этом случае приведенный код имеет два главных недостатка – во-первых, клиент для этого должен так же поддерживать удаленные вызовы, во-вторых, он неэффективен, если удаленный метод постоянно вызывает находящуюся на стороне клиента функцию. 31
Тема 3. Описание интерфейса программной компоненты Для решения возникшей проблемы в общем случае требуется передать тем или иным способом кода функции (и, возможно, используемых ею данных) на сервер с реализующим математический метод удаленным объектом. Такой подход неприменим в общем случае по соображениям безопасности, но
при необходимости можно,
например, при использовании .NET Framework передать клиенту сборку с требуемым кодом как данные при удаленном вызове. В качестве возможной альтернативы для математических задач может выступать и передача кода функции для дальнейшей компиляции встроенным компилятором языка C#. 2.5.
Распределенные события
При разработке программного обеспечения достаточно часто возникает потребность получать извещения о каких-либо событиях, возникающих асинхронно, то есть в некоторые произвольные моменты времени. В распределенных системах так же может возникнуть необходимость использования таких извещений, получаемых от удаленной системы. Можно выделить два подхода к обработке событий – тесно связанные и слабо связанные события. При тесно связанном событии происходит прямое уведомление одной стороны другой стороной. Хотя этот метод можно использовать, например, вместе с однонаправленным асинхронным вызовом, ему свойственен ряд недостатков, ограничивающих его применение в распределенных системах: –
обе компоненты системы должны выполняться одновременно;
–
для уведомления нескольких компонент об одном событии уведомляющей стороной должны использоваться механизмы для ведения списка получателей событий;
–
затруднена фильтрация или протоколирование событий.
Поэтому в распределенных системах так же применяются слабо связанные события, когда источники события (издатели) не взаимодействуют напрямую с получателями событий (подписчиками). Промежуточная среда в этом случае должна предоставить сервис, позволяющий подписчику подписаться на какое-либо событие или отказаться от подписки, а издателю – инициировать событие для рассылки подписчикам (рис 2.8).
32
Тема 3. Описание интерфейса программной компоненты
Подписчик Подписчик события Подписчики события события
Промежуточная среда
Уведомления
Событие
Издатель
Рисунок 2.8. Подписчики и издатели слабосвязанных событий При использовании слабосвязанных событий подписчики, издатели и менеджер событий могут располагаться на различных компьютерах. Само событие может быть реализовано
как,
например,
вызов
менеджером
событий
некоторого
зарегистрированного метода удаленного объекта. 2.6.
Распределенные транзакции
Транзакция – последовательность операций с какими-либо данными, которая либо успешно
выполняется
невозможности
полностью,
успешно
либо
выполнить
все
не
выполняется действия
вообще.
происходит
В
случае
возврат
к
первоначальным значениям всех измененных в течение транзакции данных (откат транзакции). Транзакция должна обладать следующими качествами. –
Атомарность. Транзакция выполняется по принципу «все или ничего».
–
Согласованность. После успешного завершения или отката транзакции все данные находятся в согласованном состоянии, их логическая целостность не нарушена.
–
Изоляция. Для объектов вне транзакции не видны промежуточные состояния, которые могут принимать изменяемые в транзакции данные. С точки зрения «внешних» объектов, до успешного завершения транзакции они должны иметь то же состояние, в котором находились до ее начала.
–
Постоянство. В случае успешности транзакции сделанные изменения должны иметь постоянный характер (т.е. сохранены в энергонезависимой памяти).
33
Тема 3. Описание интерфейса программной компоненты
Пользователь
ИП
ЛП ДД
ЛП ДД
ЛП ДД
БД
БД
Участники распределенной транзакции
Рисунок 2.9. Распределенная транзакция Транзакции являются основой приложений, работающих с базами данных, однако в распределенной системе может быть недостаточно использования только транзакций систем управления базами данных. Например, в распределенной системе в транзакции может участвовать несколько распределенных компонент, работающих с несколькими независимыми базами данных (рис. 2.9). Распределенной
называется
транзакция,
охватывающая
операции
нескольких
взаимодействующих компонент распределенной системы. Каждая из этих компонент может работать с какими-либо СУБД или иными службами, например, использовать очереди сообщений, или даже работать с файлами. При откате транзакции все эти операции должны быть отменены. Для этого необходимо выполнение двух условий: –
промежуточная среда должна поддерживать управление распределенными между несколькими компонентами транзакциями;
–
компоненты распределенной системы не должны работать с какими-либо службами или ресурсами, которые не могут участвовать в транзакции.
Распределенные
транзакции
являются
важнейшим
элементом
поддержания
целостности данных в распределенной системе. Поэтому для более широкого их применения промежуточная среда может содержать механизмы, которые при необходимости (и определенных затратах времени на написание кода) позволят использовать в распределенных транзакциях внешние службы, не поддерживающие транзакции. Такой механизм называется компенсирующим менеджером ресурса 34
Тема 3. Описание интерфейса программной компоненты (compensating resource manager). Компенсация в данном случае означает возврат ресурса к первоначальному состоянию при откате транзакции. В настоящее время происходит формирование и стандартизация еще одного понятия, связанного с поддержкой целостности данных – хозяйственной деятельности (business activity) применительно к распределенным системам. Деятельность обычно является отражением некоторого реального процесса, например, покупки в магазине: от оформления заказа до подтверждения доставки курьером. Деятельность может включать в себя транзакции (оформление заказа покупателя, заказ товара у поставщика, и так далее – до подтверждения доставки покупателем). В отличие от транзакции, время жизни которой предполагается коротким, деятельность может длиться в течение очень долгого времени (например, месяца). Деятельность может поддерживать отмену сделанных изменений (например, оформление возврата товара поставщика при отказе покупателя) путем использования компенсирующих задач. 2.7.
Безопасность в распределенных системах
Для обеспечения безопасности распределенной системы промежуточная среда должна обеспечивать поддержку трех общеизвестных функций, необходимых для создания безопасных систем. 1. Проверка
подлинности
пользователя
сервисов
компоненты
распределенной
системы (аутентификация1). Проверка подлинности может быть односторонней, когда только сервер убеждается в подлинности клиента, или двухсторонней, когда клиент так же убеждается в подлинности сервера. 2. Ограничение доступа к сервисам компоненты в зависимости от результатов аутентификации (авторизация). Для решения данной задачи промежуточная среда должна поддерживать ограничение доступа, основанное на так называемых ролях (role-based security). Поскольку разработчики компоненты не могут обозначить уровни
1
Аутентификация является стандартным термином в русской литераторе, хотя данный термин является
«склейкой» английских authentication и identification.
35
Тема 3. Описание интерфейса программной компоненты доступа через конкретных пользователей или групп пользователей системы, то они должны использовать некоторые абстрактные роли, которые при развертывании компоненты будут связаны администратором системы с учетными записями пользователей системы. 3. Защита данных, передаваемых между компонентами системы, от просмотра и изменения третьими сторонами. Для этого передаваемые между компонентами сообщения должны подписываться электронной подписью и шифроваться как клиентом, так и сервером. Функции обеспечения безопасности могут обеспечиваться транспортным протоколом, используемым промежуточной средой, самой средой, или ими обеими в совокупности. 2.8.
Промежуточные среды в Microsoft .NET Framework
Понятие промежуточной среды, обеспечивающей сервисы высокого уровня для инкапсуляции удаленного взаимодействия, появилось в середине 90-х годов, когда выяснилось, что для создания распределенных систем необходима некоторая независящая от приложения и операционной среды «прослойка». Среда CLR так же может рассматриваться как некоторая «промежуточная» среда для выполнения программ на управляемом коде. Поэтому закономерно использовать .NET Framework в качестве основы для создания распределенных приложений. В настоящий момент в .NET Framework Class Library присутствует поддержка четырех промежуточных сред для построения распределенных систем. Далее они перечислены в порядке даты выпуска промежуточной среды. –
Среда Microsoft Message Queuing (MSMQ) поддерживает обмен сообщениями между программными компонентами на основе очередей.
–
Среда Microsoft Enterprise Services основана на разработанной ранее фирмой Microsoft среде COM+, которая позволяет использовать удаленные объекты и распределенные транзакции в локальной сети.
–
Среда ASP .NET Web Services позволяет организовать удаленный вызов на основе общепринятых стандартов, базирующихся на языке XML. 36
Тема 3. Описание интерфейса программной компоненты –
Среда .NET Remoting была разработана как универсальная промежуточная среда для использования удаленных объектов.
В версии .NET Framework 3.0 предполагается ввести технологию WCF (Windows Communication Foundation), объединяющую все упомянутые технологии построения распределенных систем. Кроме указанных технологий, приложения на .NET Framework могут использовать, например, удаленные вызовы на основе стандарта XML-RPC при подключении дополнительных библиотек.
37
Тема 3. Описание интерфейса программной компоненты
Тема 3. Описание интерфейса программной компоненты Рассматривается понятие интерфейса компоненты, языков описания интерфейса и сообщений, передаваемых между программными компонентами. Описываются существующие спецификации данных языков.
3.1.
Сервисы и интерфейс программной компоненты
Для работы с сервисами программной компоненты обращающийся к ним клиент должен иметь полное представление об интерфейсе используемой компоненты. Несмотря на значительные отличия модели передачи сообщений и модели удаленного вызова, для них обеих интерфейс компоненты распределенной системы можно описать как совокупность адресов и форматов сообщений ее сервисов. В роли сервиса, предоставляемой программной компонентой, созданной с помощью .NET Framework, выступает одно из следующих понятий: –
методы активируемого сервером объекта;
–
активируемый клиентом объект вместе со своими полями, свойствами и методами;
–
очередь
с
сообщениями-запросами,
которые
считываются
программной
компонентой. Адрес сервиса зависит от промежуточной среды и является совокупностью сетевого адреса компоненты и некоторого публичного имени сервиса. Сетевой адрес программной компоненты основан на имени ее компьютера для систем удаленного вызова или на адресе менеджера очереди для систем обмена сообщениями. Данный адрес является адресом нижестоящего протокола, на котором основана данная промежуточная среда. В роли него может выступать HTTP, TCP, NetBIOS, или протокол нижестоящей промежуточной среды. Второй составляющей адреса сервиса является идентификатор сервиса. В роли него может выступать некий идентификатор активируемого класса для сред удаленного вызова или же имя очереди сообщений, из которой сервис считывает сообщения-запросы. Хотя имя вызываемого метода часто фактически описывается в самом сообщении, его следует рассматривать как составную 38
Тема 3. Описание интерфейса программной компоненты часть адреса сервиса, поскольку форматы сообщений, очевидно, различаются для различных методов одного и того же класса. Если компонента системы передачи сообщений посылает сообщения-ответы клиенту, то можно считать, что сервис такой компоненты имеет два адреса – один для очереди запросов и второй для очереди ответов (имя очереди ответов может быть задано и в сообщении-запросе). Кроме информации о полном адресе сервиса, клиенту компоненты необходимо знать формат сообщений, получаемых и возвращаемых сервисом. К первым относятся сообщения с параметрами удаленного вызова и сообщения-запросы в очередях сообщений, ко
вторым
–
сообщения
с
результатом
выполнения
метода и
сообщения-ответы. К параметрам удаленного метода следует отнести и некоторый идентификатор активированного объекта сервера для случая активации объектов по запросу клиента. Можно постулировать, что каждому сервису компоненты должна соответствовать единственная спецификация формата принимаемых им сообщений и единственная спецификация принимаемых от него сообщений (в частном случае это спецификация информирует об отсутствии ответа компоненты). Важным различием систем обмена сообщениями от систем удаленного вызова является отсутствие ограничений на формат сообщения. Таким образом, формально в них существует возможность использования для описания формата сообщения, например, контекстно-свободных формальных грамматик. Однако было бы естественным считать, что формат сообщения должен быть эквивалентен описанию полей некоторого класса CLI. Объект данного класса преобразуется в результате сериализации в передаваемое сообщение. Если и каждое сообщение в системах очередей сообщений, и параметры удаленного вызова метода будут представлять собой единственный сериализованный объект некоторого сложного типа данных, то различие между системами с активируемыми сервером объектами и системами передачи сообщений становится минимальным. Кроме того, ранее было показано, что единственный параметр удаленного вызова является хорошим решением проблемы недоступности свойств активируемых сервером 39
Тема 3. Описание интерфейса программной компоненты объектов. Поэтому существует рекомендация создавать удаленные методы с единственным параметром сложного типа. Этот объект должен маршализироваться по значению, как и все его поля и свойства. Итого, каждый сервис программной компоненты характеризуется тремя сущностями: –
полным адресом сервиса;
–
единственной спецификацией принимаемых сервисом сообщений (запросов);
–
единственной спецификацией принимаемых от сервиса сообщений (ответов).
Совокупность спецификаций всех сервисов программной компоненты образует ее интерфейс (рис. 3.1). Спецификация интерфейса программной компоненты
Адрес компоненты
Спецификация сервиса Формат Формат Формат Формат Идентификатор Формат Формат Идентификатор входящего исходящего Идентификатор входящего исходящего сервиса входящего исходящего сервиса сообщения сообщения сервиса сообщения сообщения сообщения сообщения
Рисунок 3.1. Интерфейс компоненты распределенной системы Для полного формального описания взаимодействий двух компонент распределенной системы необходимы в общем случае три языка: –
язык
передаваемых
в
распределенной
системе
сообщений,
обычно
описывающий результат сериализации объектов; –
язык
описания
спецификаций
сообщений,
определяющий
корректные
сообщения для сервисов компоненты; –
язык описания интерфейса компоненты, определяющий набор ее сервисов.
Языки описания интерфейса и спецификаций сообщений часто представлены на практике одним языком. Поскольку сообщение обычно представлено результатом сериализации того или иного класса, то одной из спецификаций сообщения можно считать совокупность сериализуемых полей и свойств маршализируемого по значению объекта. Для систем удаленного вызова спецификацией интерфейса может являться описание класса .NET. 40
Тема 3. Описание интерфейса программной компоненты Таким образом, метаданные из сборок с описанием интерфейса или класса удаленного объекта и классами параметров его методов полностью определяют интерфейс программной компоненты, созданной при помощи .NET Framework. Однако такой подход часто неудобен, поскольку не только уменьшает открытость системы, привязывая описание интерфейса программной компоненты к используемому для ее создания средству разработки, но и требует предоставления в общем случае сборок с классами компоненты клиенту. Поэтому существует потребность в общепринятых и независимых от средств разработки программных компонент языках описания интерфейса компоненты. 3.2.
Язык XML и схемы XML
Язык XML (Extensible Markup Language) в настоящее время нашел множество разнообразных применений и является основой для большого числа общепринятых спецификаций. На рис. 3.2 представлены основные используемые в распределенных системах спецификации, основанные на языке XML – XSD, SOAP, WSDL, – которые будут рассмотрены далее. XML: язык разметки
XSD: описание схемы документа
WSDL: спецификация сервиса
SOAP: спецификация сообщений
Рисунок 3.2. XML и основанные на нем спецификации Язык XML представляет собой язык разметки текстового документа, представленного совокупностью именованных, древовидно вложенных элементов. Каждый элемент может иметь некоторое текстовое значение и набор атрибутов, имеющих имя и простое 41
Тема 3. Описание интерфейса программной компоненты значение
(строку).
Язык
XML
является
абстрактным
языком
разметки,
не
определяющим как-либо смысл элементов документа. Документы XML достаточно хорошо
читаются
как
человеком,
так
и
многочисленными
программными
анализаторами. При естественном подходе к именам элементов и атрибутов он является самодокументирующимся языком. Перед древовидной структурой элементов, имеющих единственный корень, могут идти отдельные элементы с метаинформацией, указывающий в частности кодировку документа и версию языка, как показано в следующем примере.
Основными недостатками XML с точки зрения обмена сообщениями является неудобное, в силу его древовидной структуры, представление отношения «многие ко многим», а также несколько большие затраты времени на передачу и разбор сообщений на языке XML по сравнению с двоичным представлением аналогичных данных. Поскольку свойственное XML открытое представление информации не всегда удобно с точки зрения безопасности, то существует спецификации XML-DigitalSignature и XML-Encrypton,
предназначенные
для
передачи
в
XML
конфиденциальной
информации. Первая из них позволяет добавить к XML-документу цифровую подпись, вторая – зашифровать XML-документ или отдельные его элементы. Для определения назначения элементов и атрибутов XML-документа введено понятие пространства имен XML (XML namespace), которое должно иметь уникальный идентификатор. Обычно пространство имен идентифицируется некоторым URI (Uniform Resource Identifier), связанным с доменом организации, предложившее пространство имен. По данному URI может располагаться некоторое описание пространства имен, однако это не обязательно. Все используемые в XML-документе пространства имен описываются в корневом элементе документа в атрибутах с именем вида xmlns:schema_id. Таким образом схема связывается с некоторым коротким 42
Тема 3. Описание интерфейса программной компоненты идентификатором schema_id, который затем используется как префикс атрибутов и элементов.
Например,
в
следующей
строчке
пространству
имен
http://www.w3.org/2001/XMLSchema дается идентификатор xs, используемый в имени элемента xs:schema.
Часть имени элемента после двоеточия является локальной, а часть перед двоеточием должна быть связана со схемой в корневом атрибуте. Благодаря такой нотации разные пространства имен могут иметь совпадающие имена элементов. Пространства имен используются оперирующими с XML программами. Одним из достоинств XML является наличия языков спецификаций, определяющих правильный XML-документ. Первоначально эту функцию выполнял DTD (Document Type Definition), однако в настоящий момент общепринятым стандартом является спецификация схем XML (XML Schema Definition, XSD). XSD-документ является также документом
на
языке
XML,
использующим
пространство
имен
http://www.w3.org/2001/XMLSchema. Файл с описанием схемы XML определяет: –
словарь документа (имена элементов и атрибутов);
–
синтаксис корректного документа;
–
сложные типы данных.
Элементарные
типы
данных
описаны
в
самом
пространстве
имен
http://www.w3.org/2001/XMLSchema. Синтаксис схемы позволяет описать сложные типы данных, включающие в себя поля простых типов, сложных типов, а также сложные последовательности, каждый элемент которых может принадлежать к одному из нескольких сложных или простых типов данных. Таким образом, синтаксис схемы XML позволяет описать результат сериализации контейнера (списка или массива), в котором могут храниться объекты нескольких известных классов. В состав .NET Framework входит утилита xsd.exe, создающая схему по заданному классу, которая будет подробно рассмотрена в следующей главе. Соответствие между классами .NET и схемами не является взаимно однозначным. В частности, при использовании утилиты xsd.exe
любым
видам
списочных
контейнеров
соответствует
одинаковая
последовательность в схемах XML. 43
Тема 3. Описание интерфейса программной компоненты 3.3.
SOAP: язык сообщений распределенной системы
Стандартизация описания языка XML дала широкие возможности для построения на его основе языков описания сообщений, передаваемых между программными компонентами, и языков описания сервисов программной компоненты. В конце 90-х годов началась разработка двух спецификаций для построения распределенных гетерогенных систем – SOAP и XML-RPC. Спецификация XML-RPC поддерживается в настоящий момент большим числом языков, но имеет меньше возможностей и не поддерживается стандартной библиотекой .NET Framework. Поскольку в момент разработки данных спецификаций протокол HTTP был как наиболее распространенным, так и повсеместно разрешенным в межсетевых экранах протоколом, то он был выбран в качестве стандартного транспортного протокола для создания гетерогенных промежуточных сред. В силу этого, хотя спецификация SOAP не привязана жестко к какому-либо транспортному протоколу, использующая SOAP и WSDL промежуточная среда получила названия веб-служб (web services). Она использует
два
дополнительных
языка
–
язык
описания
сообщения
SOAP
(пространство имен SOAP версии 1.1 – http://schemas.xmlsoap.org/soap/envelope/, версии 1.2 – http://schemas.xmlsoap.org/wsdl/soap12/) и язык описания сервисов и интерфейсов WSDL (пространство имен http://schemas.xmlsoap.org/wsdl/). Рекомендация SOAP изначально разрабатывалась как спецификация для удаленного вызова методов и расшифровывалась как Simple Object Access Protocol. Сообщение SOAP представляет собой XML-документ, называемый конвертом или пакетом (envelope), содержащий заголовки с метаинформацией в элементе soap:Header и тело сообщения в элементе soap:Body. В заголовках пакета содержится дополнительная информация, которая может использоваться промежуточной средой. Благодаря тому, что основной стандарт не ограничивает содержание заголовков, SOAP является расширяемой спецификацией, и в настоящее время все еще идет процесс стандартизации ее расширений. Что касается представления тела сообщения, то в силу различных причин в настоящий момент существует два различных способа представления информации в теле пакета 44
Тема 3. Описание интерфейса программной компоненты SOAP – кодирование SOAP-RPC (в двух вариантах) и кодирование SOAP-Document. Кодирование SOAP-RPC предназначено исключительно для передачи параметров удаленного вызова и определяет сообщение как имя метода и список параметров. При использовании
кодирования
SOAP-Document,
которое
является
фактическим
стандартом в настоящий момент, сообщение представляет собой XML-документ со схемой и пространством имен, заданными в описании сервиса на языке WSDL. Хотя обычно сообщение и состоит из имени метода удаленного объекта и списка его параметров, но сама спецификация кодирования не фиксирует как-либо его содержание. В качестве примера рассмотрим простейшие сообщения SOAP версии 1.2 для сервиса, складывающего
два
числа.
Сообщение-запрос
использует
пространство
имен
http://summa.test/webservices, которое описано в интерфейсе компоненты, что будет показано далее. В элементе message содержатся сами складываемые числа. 1 2
Сообщение с ответов программной компоненты. 3
3.4.
WSDL: описание интерфейса программной компоненты
Для описания интерфейса программной компоненты, включая спецификацию корректных сообщений, был разработан язык WSDL (Web Service Definition Language). Описание на языке WSDL включает в себя следующие семь составляющих (рис. 3.3). 45
Тема 3. Описание интерфейса программной компоненты Описание типов данных
Описание абстрактных операций
Типы данных Сообщения
Операции Типы портов Привязки
Описание интерфейса
Порт Служба
Рисунок 3.3. Составные части WSDL-документа 1. Описание
типов
передаваемых
данных.
При
использовании
кодирования
SOAP-Document оно состоит из схемы XML, определяющей корректные сообщения, получаемые программной компонентой в теле пакета SOAP. 2. Описание входящих и исходящих сообщений, которые связываются с описанными типами данных. 3. Описание операций (сервисов программной компоненты), с каждой из которых связывается входящее и исходящее сообщение. 4. Описание типов портов (идентификаторов программных компонент), с каждым из которых связывается некоторый набор операций. 5. Описание привязок (binding), связывающие типы портов и их сообщений с определенным типом кодирования тела пакета, а также с версией протокола SOAP. 6. Описание портов, связывающие типы портов и соответствующие им привязки с конкретными URL. 7. Общее описание службы (интерфейса программной компоненты) как совокупности портов. Далее рассмотрено описание на языке WSDL интерфейса компоненты, которое содержит два сервиса – сложение двух чисел и сложение последовательности чисел. В 46
Тема 3. Описание интерфейса программной компоненты корневом
элементе
указаны
все
используемые
пространства
имен,
включая
пространство протокола SOAP 1.2 – http://schemas.xmlsoap.org/wsdl/soap12/.
В элементе
wsdl:types описываются все типы данных. Тип Add будет связан со
входящим сообщением сервиса сложения двух чисел, а тип AddResponse – с его исходящим сообщением.
Типы SumList и SumListResponse предназначены для сообщений сервиса сложения списка чисел.
47
Тема 3. Описание интерфейса программной компоненты
В элементах wsdl:message типы данных связываются с идентификаторами сообщений.
В элементе wsdl:portType описываются абстрактные операции и используемые ими сообщения. Операция Add складывает два числа Операция SumList складывает несколько чисел
В элементе wsdl:binding операции связываются с транспортным протоколом (HTTP), версией протокола SOAP (1.2) и типом кодирования тела пакета (SOAP-Document).
48
Тема 3. Описание интерфейса программной компоненты
В элементе wsdl:service интерфейс программной компоненты связывается с типом порта, с некоторой привязкой, а также с конкретным URL, используемым в дальнейшем для вызова веб-службы.
Как видно из примера, структура документа на языке WSDL является достаточно сложной, однако благодаря ей одни и те же абстрактные операции могут быть связаны с различными способами передачи информации, включая как разные транспортные протоколы, так и различные версии спецификации SOAP. Для реализации удаленного вызова на основе данной спецификации необходимы средства как для создания WSDL-документа по описанию используемого удаленно класса, так и средство создания по известному WSDL-документу посредника удаленного вызова. 3.5. Для
Выводы по описанию интерфейса компоненты создания
открытой
распределенной
системы
необходимо
использование
общепринятых языков описания интерфейса программной компоненты. В настоящий момент существует ряд апробированных на практике стандартов для передачи данных 49
Тема 3. Описание интерфейса программной компоненты в гетерогенных распределенных системах: XML, XSD, SOAP и WSDL. Их использование позволяет создавать системы, не привязанные жестко к какому-либо средству разработки программ или транспортному протоколу. Однако открытый характер
спецификации
SOAP
допускает
как
реализацию
использующей
ее
промежуточной средой некоторой дополнительной функциональности, так и принятие все новых стандартов и расширений, использующих заголовки SOAP. Это может привести к определенным сложностям при взаимодействии основанных на WSDL и SOAP программных компонент различных разработчиков.
50
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Рассматривается понятие графа объектов и проблема его передачи между двумя компьютерами. Описывается проблема сериализация графа объектов, приводится классификация методов сериализации. Приводятся описание различных методов сериализации, которые используются в .NET Framework, и их особенностей.
4.1.
Сериализация графа объектов
В отличие от приложений на неуправляемом коде, приложения .NET Framework не обязательно выполняются в виде отдельных процессов, а могут существовать в пределах одного процесса операционной системы в своих собственных областях, называемых доменами приложения. Такие области можно рассматривать как некоторые
логические
процессы
виртуальной
машины
CLR.
Использование
управляемого кода позволяет при этом гарантировать изоляцию приложений в пределах своих областей. При передаче между доменами приложений некоторого объекта для его класса должна быть определена процедура сериализации, которая позволяет сохранить состояние объекта в некотором внешнем хранилище (например, в файле, или в сообщении транспортного протокола) при помощи потоков ввода-вывода, и процедура десериализации, создающая копию объекта по сохраненному состоянию (рис 4.1). Следует отметить, что в общем случае это могут быть объекты разных классов, и даже созданные в разных системах разработки приложений. Домен приложения 1
Домен приложения 2
Объект
Копия объекта
Сериализация
Десерилизация
Поток вывода
Хранилище
Поток ввода
Рисунок 4.1. Сериализация и десериализация объекта 51
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Задача
сериализации
объекта,
включающего
только
поля
из
элементарных
типов-значений (наследников класса System.ValueType) и строк, не представляет принципиальных трудностей. Для такого объекта в ходе сериализации в поток записываются сами значения всех полей объекта. Однако в общем случае объект содержит ссылки на другие объекты, которые, в свою очередь, могут ссылаться друг на друга, образуя так называемый граф объектов (object graph). Сами ссылки не могут быть сохранены в потоке ввода-вывода, поэтому основной вопрос сериализации – это способ замены ссылок. Граф объектов – ориентированный граф G = < V, E > , в котором вершины – это объекты (множество V), а ребра направлены от объектов, содержащие ссылки, на ссылаемые объекты (рис 4.2). public class SampleClass { public SampleClass fieldA = null; public SampleClass fieldB = null; } ... SampleClass root = new SampleClass(); SampleClass child1 = new SampleClass(); SampleClass child2 = new SampleClass(); root.fieldA = child1; root.fieldB = child2; child1.fieldA = child2; ...
root
child 1
child 2
Рисунок 4.2. Граф объектов Наличие ссылки на объект B в объекте A есть принадлежность пары < A, B > множеству E.
При
рассмотрении
графа
все
поля
объектов,
относящиеся
к
простым
типам-значениям и строкам, можно исключить из рассмотрения в силу тривиальности их сериализации. Хотя формально строки являются ссылочными типами, особенность реализации строк в CLI не дает возможность изменить уже созданную строку. Эта
52
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework особенность позволяет тривиально обрабатывать строки при сериализации, сохраняя в потоке их содержимое. Существует частный случай, когда сериализация выполняется тривиально. Для этого граф должен иметь вид так называемого ориентированного дерева. В каждую вершину дерева, отличную от корня, направлено единственное ребро, и существует единственная вершина, в которую не ведет ни одного ребра, называемая корнем. В таком случае сериализация может вестись от корня методом в глубину, а сериализация полей-ссылок
выполняется
аналогично
сериализации
полей-значений
–
при
обнаружении ссылки на объект вместо нее в хранилище помещаются значения полей ссылаемого объекта и некоторая дополнительная информация о типе объекта. Например, если существуют ребра < A, B1>, ... < A, Bn >, функция сериализации S для объекта A может быть определена как S(A) = < V(A), >, где функция V – значение полей-значений и строк указанного объекта. Проблема сериализации графа объектов связана с тем, что ссылка на один и тот же объект может быть значением поля у разных объектов (существуют ребра < A1, B> и < A2, B >). Возможно также наличие циклов вида A → ... → A. В этом случае в ходе сериализации объектам должны быть поставлены в соответствие некоторые идентификаторы, и в хранилище отдельно сохраняется список объектов, отмеченный идентификаторами, а при сериализации вместо ссылок записываются идентификаторы ссылаемых объектов. Если A1, ... An – все вершины графа, то его образом после сериализации будет множество {< idA1, S(A1) >, ... }, где S(A) определена как S(A) = < V(A), >, где B1, ... Bn – объекты, ссылки на которые непосредственно содержатся в объекте A. В программе роль идентификатора объекта выполняет его адрес, но вместо него обычно удобнее назначить некоторые идентификаторы в процедуре сериализации для более легкого чтения человеком полученного образа. В ходе сериализации нужно ввести список адресов уже записанных объектов, как для ведения списка идентификаторов, так и для обнаружения возможных циклов при обходе графа методом в глубину.
53
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Следует отметить, что результат сериализации дерева легко представим в виде XML, когда содержимое каждого объекта представлено одним элементом с некоторым набором атрибутов и вложенных элементов. Поэтому при рассмотрения проблемы сериализации можно сформулировать рекомендацию, что классы, передаваемые между удаленными компонентами, должны являться корнем дерева объектов. В частном случае это дерево может быть вырожденным, то есть класс не содержит полей-ссылок вообще. 4.2.
Методы сериализации в .NET Framework
Microsoft .NET Framework 2.0 поддерживает
четыре
технологии удаленного
взаимодействия – Message Queuing, Enterprise Services, Remoting, Web services. Какая бы технология не была выбрана, в любом случае передача данных между удаленными компонентами является одним из основных моментов удаленного взаимодействия. Данные могу передаваться как сообщение в очереди (MSMQ) или как параметры удаленного вызова и результат вызываемой функции (остальные технологии). В любом случае, для реализации распределенной системы необходимо определить процедуры сериализации и десериализации для передаваемых классов. Классы, производящие сериализацию и десериализацию в .NET Framework, называются классами форматирования (formatters). Не считая служащего для обеспечения совместимости ActiveXMessageFormatter, в .NET Framework выделяется три различных независимых класса форматирования: XmlSerializer, SoapFormatter, BinaryFormatter. Они используются как для записи и чтения объектов из потоков ввода-вывода, так и для построения распределенных систем: –
технология веб-служб ASP.NET использует XmlSerializer;
–
технология Remoting использует SoapFormatter, BinaryFormatter или созданный пользователем класс;
–
при
работе
с
сообщениями
MSMQ
используется
XmlSerializer
(через
XMLMessageFormatter), или BinaryFormatter (через BinaryMessageFormatter), или созданный пользователем класс; –
технология
Enterprise
Services
основана
на
Remoting
и
использует
BinaryFormatter. 54
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Можно провести классификацию возможных методов сериализации по следующим основным признакам. 1. Классификация по виду обрабатываемого графа: –
универсальные методы: граф произвольного вида с циклами;
–
произвольный ациклический граф;
–
дерево.
2. Классификация по формату хранения информации в хранилище: –
бинарные методы, использующие двоичный формат хранения данных, который не пригоден для чтения человеком без использования специальных средств;
–
текстовые методы, используют XML или иные текстовые форматы, пригодные для чтения или редактирования человеком при использовании текстового редактора.
3. Классификация по спецификации формата данных, полученного в результате сериализации: –
закрытые методы: спецификация задается только при помощи интерфейсов всех классов, объекты которых образуют граф; в этом случае обе удаленные компоненты должны быть созданы на одной языковой платформе, причем обе стороны должны иметь как минимум описание интерфейса сериализуемых классов;
–
открытые методы: спецификация может быть задана в виде общепринятого формата, например схемы XSD.
По
данной
классификации
XmlSerializer
реализует
открытый
текстовый
неуниверсальный метод, BinaryFormatter – закрытый двоичный универсальный метод, SoapFormatter – текстовый открытый метод. Дополнительной особенностью каждого из классов, за исключением BinaryFormatter, является ограничения на классы, которые подлежат сериализации. Данные ограничения будут рассмотрены далее.
55
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework 4.3.
Класс сериализации XmlSerializer
Класс System.Xml.Serialization.XmlSerializer реализует открытый текстовый метод сериализации, использующий XML в качестве базового формата хранения и схемы XML для спецификации документа с результатом сериализации. Данный класс используется, в частности, в веб-службах ASP.NET и при работе с очередями сообщений MSMQ. Класс XmlSerializer порождает представление объекта на языке XML, легко читаемое и модифицируемое человеком или программами. Класс XmlSerializer позволяет управлять соответствием класса и схемы XML при помощи специальных атрибутов полей классов. Благодаря открытой спецификации формата хранения, при использовании XmlSerializer возможна десериализация в тип данных, отличный от исходного, как и написание взаимодействующих компонент на основе средств разработки. Схема XML может быть получена из описания класса, если имеется информация о всех типах объектов, которые могут содержаться во всех полях каждого класса. В частности, это означает что для класса общего вида (generic) XML-схема в общем случае не может быть получена. Однако и для конкретных классов самого описания типа недостаточно для создания схемы. Например, в поле вида IVehicle vehicleValue может содержаться объект любого класса, реализующий интерфейс IVehicle. Данная проблема решается в CLI
благодаря
системе
атрибутов.
Например,
используя
атрибут
System.XML.Serialization.XmlAttrbute, можно перечислить все возможные типы объектов
данного
поля.
Хотя
такой
подход
не
идеален
с
точки
зрения
объектно-ориентированного подхода, он дает возможность строго специфицировать содержимое
XML-файла.
Ниже
приведен
пример
использования
атрибута
XmlAttrbuteAttrbute. [System.XML.Serialization.XmlAttrbute(typeof(Car)), System.XML.Serialization.XmlAttrbute(typeof(Bike)), System.XML.Serialization.XmlAttrbute(typeof(LongTruck))] IVehicle vehicleValue;
Таким образом, при использовании некоторых дополнительных метаданных, для классов CLI может быть построена соответствующая им схема XML, которая будет корректной для организованного древовидно графа объектов. 56
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Используемый классом XmlSerializer метод сериализации имеет ряд недостатков. Во-первых, он не является универсальным: сериализуемый им граф объектов не может содержать циклы. Во-вторых, он полагает, что граф объектов является деревом и записывает значение полей объекта на место их ссылки. В результате десериализации создаются столько копий объектов, сколько в соответствующем графе в него входило ребер (рис 4.3). Поскольку на этапе построения XML-схемы нет никакой информации о каких-либо объектах, а только описания полей и свойств классов, то предполагается, что на каждый объект сериализуемого графа, отличный от его корня,
существует
единственная ссылка в поле какого-либо другого объекта этого же графа. Исходный граф
Результат десериализации
root
child 1
root
child 2
child 2
Исходный граф объектов Сериализация
child 1
копия child 2
Копия графа объектов Хранилище
Десериализация
Рисунок 4.3. Применение XmlSerializer к произвольному графу объектов Наибольшей
трудностью
при
использовании
класса
XmlSerializer
являются
предъявляемые им требования к сериализуемым классам. В .NET Framework 2.0 XmlSerializer позволяет сериализовать публичные классы, имеющие конструктор без параметров типа public и отвечающие одному из следующих требований. 1. Класс реализует интерфейс IXMLSerializable. В этом случае XMLSerializer просто использует при сериализации методы класса GetSchema, ReadXml, WriteXml. 2. Класс реализует интерфейс System.Collections.IEnumerable, но не реализует ICollection и содержит публичный метод Add c единственным параметром, имеющим тип,
совпадающий
с
типом
результата
свойства
IEnumerator.Current
метода
GetEnumerator сериализуемого объекта. Такой класс сериализуется через вызовы класса 57
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework IEnumerator, возвращаемого методом GetEnumerator, а его публичные поля и свойства не сериализуются. 3. Класс
реализует
интерфейс
System.Collections.ICollection,
но
не
реализует
IEnumerable. Для такого класса осуществляется сериализация только свойства Item и публичных полей, реализующих интерфейс ICollection. Другие публичные поля и свойства не сериализуются. 4. Класс реализует интерфейсы ICollection и IEnumerable, имеет публичное индексированное свойство Item c целым индексом и публичное целое свойство Count. Тип, принимаемый методом Add, должен быть типом свойства Item или одним из его предков. Другие публичные поля и свойства не сериализуются. 5. Класс не реализует ни один из интерфейсов IXMLSerializer, IEnumerable, ICollection и имеет атрибут System.SerializableAttribute. В этом случае будут сериализованы публичные свойства и поля класса с учетом специальных атрибутов, управляющих процессом сериализации. Подлежащие сериализации публичные свойства должны иметь реализацию обоих методов, get и set. Кроме того, если класс не использует собственную процедуру сериализации, то он не должен иметь свойств или полей типа интерфейс или многомерных массивов, вместо них следует использовать вложенные массивы. // недопустимо в сериализуемом автоматически классе public int[,] data = new int[2,2];
58
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework К сожалению, классы FCL, реализующие интерфейс IDictionary, не удовлетворяют этим требованием (их метод Add имеет два параметра). Поэтому главным практическим недостатком XmlSerializer является неспособность самостоятельно обрабатывать типы, реализующие интерфейс System.Collections.IDictionary, использующиеся для хранения пар
ключ–значение.
В
частности,
к
ним
относится
популярный
класс
System.Collections.Hashtable. Однако можно создать собственный класс, включающий в себя поле типа Hashtable и реализующий интерфейс IXmlSerializable. XMLSerializer может обрабатывать следующие важные на практике классы из FCL: –
System.XML.XmlNode
и
его
наследники
System.XML.XmlDocumеnt
и
System.XML.XmlElement, причем сериализация XML-документа является этим же XML-документом (с точностью до форматирования, не влияющего на содержимое документа); –
класс System.Data.Dataset;
–
классы общего вида (generic classes) из System.Collections.Generic, такие как List или Queue, кроме Dictionary и SortedDictionary.
Как было указанно ранее, для большинства классов можно использовать описанные в System.Xml.Serialization атрибуты сериализации, которые позволяют установить связь между полями класса и атрибутами или элементами XML, отвечающими ему. Также можно использовать атрибут NonSerializedAttribute для публичных полей или свойств, не подлежащих сериализации. В качестве примера рассмотрим пример общего класса, реализующий интерфейс IXMLSerializable для сериализации коллекции пар из ключа и значения какого-либо фиксированного типа. Класс содержит два объекта сериализации – один для обработки строк, второй для типа значений.
59
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework using System.Xml; using System.Collections; using System.Xml.Serialization; public class DictionaryCollection : DictionaryBase, IXmlSerializable { private XmlSerializer valueSerializer; private XmlSerializer keySerializer; public DictionaryCollection() { keySerializer = new XmlSerializer(typeof(String)); valueSerializer = new XmlSerializer(typeof(TValue)); }
Класс имеет индексируемое свойство, отвечающее за получение значения по его ключу, и метод добавления ключа и значения в коллекцию. public TValue this[string key] { get { return (TValue)this.Dictionary[key]; } set { this.Dictionary[key] = value; } } public void Add(String key, TValue value) { this.Dictionary.Add(key, value); }
Для сериализации классом XMLSerializer класс имеет два метода – WriteXml и ReadXML. public void WriteXml(System.Xml.XmlWriter writer) { foreach (String key in this.Dictionary.Keys) { keySerializer.Serialize(writer, key); valueSerializer.Serialize(writer, this[key]); } } public void ReadXml(System.Xml.XmlReader reader) { reader.Read(); while (reader.NodeType != XmlNodeType.EndElement) { String key = (String) keySerializer.Deserialize(reader); TValue value = (TValue) valueSerializer.Deserialize(reader); reader.MoveToContent(); Add(key, value); } }
60
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Метод GetSchema необходим для генерации XSD-файла для данного класса. Поскольку создание такой схемы для класса общего вида невозможно, метод не реализован. public System.Xml.Schema.XmlSchema GetSchema() { return null; } } // DictionaryCollection
Ниже показан фрагмент кода, использующего данный класса для записи пар строка-число в файл. DictionaryCollection dictionary = new DictionaryCollection(); XmlSerializer serializer = new XmlSerializer(typeof(DictionaryCollection)); dictionary.Add("key1", 10); dictionary.Add("key2", 20); using (StreamWriter writer = new StreamWriter("sample.xml")) { XmlTextWriter xmlWriter = new XmlTextWriter(writer); xmlWriter.Formatting = Formatting.Indented; serializer.Serialize(xmlWriter, dictionary); }
Для большинства применений нет нужды реализовывать интерфейс IXmlSerializable, и достаточно
использовать
многочисленные
атрибуты
из
пространства
System.Xml.Serialization, управляющие XML-сериализацией. К ним, в частности, относятся
XmlAttributeAttribute,
XmlArrayItemAttribute,
XmlElementAttribute,
XmlIgnoreAttribute.
Предназначение
XmlArrayAttribute, последнего
атрибута
наиболее простое, остальные же нуждаются в пояснении. Атрибуты XmlAttributeAttribute и XmlElementAttribute обычно применяют к скалярным полям класса. Два их опционных параметра – название атрибута или элемента XML с результатом сериализации, а также тип объекта. XmlElementAttribute может быть применен только к примитивным типам и строкам. Если тип объекта, хранящимся в поле, совпадает с указанным в типе или в атрибуте поля, то в XML-файле не будет использоваться атрибут типа xsi:type для указания типа объекта. Атрибуты XmlArrayAttribute, XmlArrayItemAttribute используются для контейнерных классов. К ним относятся массивы, коллекции (например, ArrayList) и коллекции общего вида (например, List). В этом случае атрибут XmlArray аналогичен 61
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework атрибуту XmlAttribute для скалярных классов, а XmlArrayItem указывает все возможные типы элементов массива или списка и соответствующие им имена элементов XML. Для корректной обработки контейнера в атрибутах XmlArrayItem должны быть указаны все типы объектов, которые могут храниться в контейнере. Если в контейнере хранится только тип, указанный явным образом при его объявлении, то данный атрибут не обязателен. Таким образом, если в списке List persons хранятся только объекты типа Person, то атрибут XmlArrayItem не обязателен. Следующий пример служит для иллюстрации применения указанных атрибутов для сериализации. // Файл figures.cs using System; using System.IO; using System.Xml.Serialization; using System.Collections.Generic; public abstract class GeomFigure { }
Абстрактный класс фигуры имеет двух наследников, представляющих точку и прямую на плоскости. Конструктор по умолчанию необходим для десериализации объектов.
62
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework public class GeomPoint: GeomFigure { private double xField, yField; [XmlAttribute("X")] public double X { get {return xField;} set {xField = value;} } [XmlAttribute("Y")] public double Y { get {return yField;} set {yField = value;} } public GeomPoint() { } public GeomPoint(double x, double y) { this.X = x; this.Y = y; } public override string ToString() { return String.Format("({0}, {1})", X, Y); } } public class GeomLine: GeomFigure { private GeomPoint aField, bField; public GeomPoint A { get {return aField;} set {aField = value;} } public GeomPoint B { get {return bField;} set {bField = value;} } public GeomLine() { } public GeomLine(GeomPoint a, GeomPoint b) { this.A = a; this.B = b; }
63
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework public override string ToString() { return String.Format("{0} {1}", A, B); } }
В списке фигур используются атрибуты XmlArrayItem для описания всех возможных типов фигур. public class GeomFigures { private List figuresField; [XmlArrayItem("Point", typeof(GeomPoint)), XmlArrayItem("Line", typeof(GeomLine))] public List Figures { get { return figuresField;} } public GeomFigures() { figuresField = new List(); } } public class App { public static void Main() { GeomFigures figures = new GeomFigures(); figures.Figures.Add(new GeomPoint(2, -1)); figures.Figures.Add(new GeomLine(new GeomPoint(-1, -1), new GeomPoint(2, 2))); XmlSerializer serializer = new XmlSerializer(typeof(GeomFigures)); using (StreamWriter writer = new StreamWriter("figures.xml")) { serializer.Serialize(writer, figures); }; } }
В результате выполнения этого примера будет создан следующий XML-файл.
64
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Важное применение атрибутов заключается в том, что они позволяют описать соответствие класса XSD-схеме получаемого в ходе сериализации документа. В состав .NET Framework входит утилита xsd.exe, позволяющая выполнять три основные задачи: –
создание частичного (partial) описания класса на С# по схеме XSD;
–
создание схемы XSD по классу С#;
–
создание схемы XSD по образцу XML-файла (правильность зависит от полноты образца).
Если для указанного выше файла выполнить следующую команду, то будет создан файл schema0.xsd со схемой XML. xsd.exe figures.exe /type:GeomFigures
Сама схема будет иметь следующий вид.
65
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Следующая команда создаст по XML-схеме файл на языке C#, который при необходимости можно использовать для десериализации созданного в примере XML-файла вместо оригинального файла примера. xsd.exe schema0.xsd /classes
Таким образом, при использовании для обмена данными между компонентами класса XmlSerializer можно как создать схему по сериализуемому классу и представить ее в качестве спецификации передаваемых данных, так и сгенерировать на языке C# код описания публичных свойств класса из XSD-схемы. На рисунке 4.4 показана схема применения XML-сериализатора в распределенных системах. В зависимости от применения класса XMLSerializer схема XML может передаваться как отдельный документ, специфицирующий формат обмена сообщениями между компонентами, или входить в описание интерфейса программной компоненты на языке WSDL. Компонента 1
Компонента 2
Сериализуемый класс
Схема XML
Класс для десериализации
Объект
Объект Канал передачи данных
Сериализация
Десериализация
Рисунок 4.4. Использование схем XML для сериализации объектов Особенностью класса XMLSerializer является то, что в ходе работы он создает сборки с кодом
сериализации
для
каждого
обрабатываемого
класса
при
вызове
его
конструктора. Например, в описанном ранее примере в памяти будет создана сборка с именем GeomFigures.XmlSerializers, что приводит к определенной однократной задержке. Если такая задержка нежелательна (например, программа не совершает повторяющейся сериализации одного и того же класса, но желает при этом осуществлять операции 66
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework максимально быстро), то при помощи утилиты sgen.exe можно заранее создать такие сборки и затем подключить их к проекту. Резюмируя краткое описание XMLSerializer, следует отметить, что несмотря на отдельные сложности его применения к некоторым классам, его использование позволяет создать открытое взаимодействия удаленных компонент со спецификацией сериализуемых классов в виде схемы XSD. При использовании XMLSerializer можно также рекомендовать вместо сложных структур данных использовать классы XmlDocument
или
Dataset,
особенно
если
такие
неподдерживаемые XML-сериализацией классы.
структуры
включают
Сериализуемый тип может быть
классом общего вида, но невозможность создания для таких классов схемы XML-документа ограничивают их применение. Особенностью класса XmlSerializer является жесткая привязка к десериализуемому типу данных, обычно передаваемому ему в качестве аргумента конструктора. 4.4.
Классы сериализации SoapFormatter и BinaryFormatter
Класс
сериализации
используется
System.Runtime.Serialization.Formatters.Soap.SoapFormatter
исключительно
в
среде
.NET
Remoting,
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
а
может
класс также
использоваться в среде MSMQ вместо XMLSerializer. Оба класса форматирования по приведенной классификации являются универсальными. Класс форматирования BinaryFormatter
реализует
двоичный
закрытый
метод
сериализации,
класс
SoapFormatter – текстовый и открытый, основанный на спецификации кодирования SOAP-RPC (пространство имен http://schemas.xmlsoap.org/soap/encoding/). При разработке .NET Framework 2.0 разработчики по некоторым данным собирались придать
классу
SoapFormatter
статус
устаревшего.
Класс
SoapFormatter
не
поддерживает одно из важных нововведений – параметризированные типы данных (generic types). Оба указанных класса в простейшем случае при сериализации сохраняют все поля класса (но не его свойства), вне зависимости от их видимости. Поля, имеющие атрибут System.NonSerializeAttribute,
игнорируются.
Класс
должен
иметь
атрибут 67
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework System.SerializableAttribute. В ходе сериализации класса форматирования используют методы класса System.Runtime.Serialization.FormatterServices. Сериализуемый класс должен содержать конструктор без параметров, который вызывается при создании нового объекта в ходе десериализации. Если же обрабатываемый класс реализует интерфейс ISerializable, то он сериализуется вызовом метода GetObjectData(SerializationInfo info, StreamingContext context) этого интерфейса, внутри которого обычно так же вызываются методы FormatterServices. Десериализация
таких
классов
осуществляется
вызовом
конструктора
ISerializable(SerializationInfo info, StreamingContext context), заполняющего поля объекта значениями из info. О завершении своей десериализации объект может получить уведомление, реализовав интерфейс
System.Runtime.Serialization.IDeserializationCallback
с
единственным
методом OnDeserialization. Полученный
таким
образом
на
первом
шаге
сериализации
объект
класса
SerializationInfо содержит имена и значения сериализуемых полей. Рассматриваемые классы форматирования, реализующие интерфейс IFormatter, преобразуют эти имена в некоторый вид, передаваемый между доменами приложения через потоки вводавывода. Рассмотрим пример создания класса с интерфейсом ISerializable и собственным механизмом сериализации. using using using using using using
System; System.IO; System.Runtime.Serialization; System.Runtime.Serialization.Formatters; System.Runtime.Serialization.Formatters.Binary; System.Reflection;
[Serializable] public class Person : ISerializable { public String name; public Person() { }
68
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Метод GetObjectData используется на первом шаге сериализации класса. В ходе его работы в объект класса
SerializationInfo добавляется информация о полях класса,
подлежащих сериализации. Для получения метаданных о полях класса используется статический метод GetSerializableMembers класса FormatterServices. public void GetObjectData(SerializationInfo info, StreamingContext context) { Type thisType = this.GetType(); MemberInfo[] serializableMembers = FormatterServices.GetSerializableMembers(thisType, context); foreach (MemberInfo serializableMember in serializableMembers) { // Не обрабатывать поля с аттрибутом NonSerializedAttribute if (!(Attribute.IsDefined(serializableMember, typeof(NonSerializedAttribute)))) { info.AddValue(serializableMember.Name, ((FieldInfo)serializableMember).GetValue(this)); } } }
Для
проведения десериализации класс содержит конструктор специального вида,
заполняющий поля класса значениями из объекта класса SerializationInfo. protected Person(SerializationInfo info, StreamingContext context) { Type thisType = this.GetType(); MemberInfo[] serializableMembers = FormatterServices.GetSerializableMembers(thisType, context); foreach (MemberInfo serializableMember in serializableMembers) { FieldInfo fieldInformation = (FieldInfo)serializableMember; if (!(Attribute.IsDefined(serializableMember, typeof(NonSerializedAttribute)))) { fieldInformation.SetValue(this, info.GetValue(fieldInformation.Name, fieldInformation.FieldType)); } } } } // Person
Ниже приведен пример использования созданного класса Person. public class SampleApp { public static void Main() { using (Stream stream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter();
69
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework Person person = new Person(); person.name = "Иван"; Console.WriteLine("Сохранено: {0}", person.name); formatter.Serialize(stream, person); stream.Position = 0; Person personRestored = (Person) formatter.Deserialize(stream); Console.WriteLine("Восстановлено: {0}", personRestored.name); } } }
Классы форматирования имеют механизм, позволяющий изменить процедуры сериализации и десериализации для объектов некоторого класса и его потомков. Это необходимо,
в
частности,
при
использовании
удаленных
объектов,
которые
маршализируются по ссылке и не пересекают границы домена приложения. Такие объекты находятся на сервере, а на стороне клиента для их использования должен быть создан некоторый посредник, реализующий весь интерфейс удаленного объекта, включая доступ к его полям и свойствам. Для реализации маршализации по ссылке к объекту форматирования через поле SurrogateSelector можно присоединить класс, реализующий интерфейс System.Runtime.Serialization.ISurrogateSelector. Он должен связывать тип удаленного объекта со специальной процедурой его сериализации и десериализации. Использование этого механизма в .NET Remoting приводит к тому, что наследники класса MarshalByRefObject не покидают своего домена приложения. При использовании же BinaryFormatter в среде MSMQ наследники MarshalByRefObject сериализуется обычным образом, поскольку использующий объекты форматирования класс BinarryMessageFormatter не создает связанный с типом MarshalByRefObject объект класса SurrogateSelector. Использование
класса
BinaryFormatter
является
наиболее
эффективным
и
универсальным, но и самым закрытым способом сериализации. Этот класс позволяет передавать между доменами приложения произвольный граф объектов, но при его использовании распределенная система теряет свойство открытости. В случае применения этого класса взаимодействующие компоненты могут быть созданы только на платформе CLI, причем обоим сторонам необходимо иметь сборку с сериализуемым типом. При использовании в качестве параметров типов из стандартной библиотеки или использующих их классов желательно, чтобы обе стороны были реализованы на одной версии CLI. Поэтому для передачи сложных типов лучше всего использовать 70
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework XML.
Однако,
стандартный
класс
System.Xml.XmlDocument
не
может
быть
сериализован классами BinaryFormatter и SoapFormatter, поскольку данный класс по неясным причинам не имеет атрибута Serializable. Для сериализации объектов класса XmlDocument проще всего преобразовать его в строку, и затем сериализовать ее. Можно так же создать наследника XmlDocument, который будет реализовывать интерфейс ISerializable. Ниже приводится пример вспомогательного класса с двумя статическими методами, преобразующими объект класса XmlDocument в строку и наоборот. Поскольку метод XmlDocument.ToString() против ожиданий не возвращает текст XML-документа и у него нет метода, обратного LoadXml, то следует использовать класс StringWriter. // SevaXmlUtils.cs using System; using System.IO; using System.Xml; namespace Seva.Xml { public static class XmlUtils { public static String XmlToString(XmlDocument xml) { StringWriter xmlLine = new StringWriter(); xml.Save(xmlLine); return xmlLine.ToString(); } public static XmlDocument XmlFromString(String xmlLine) { XmlDocument xml = new XmlDocument(); xml.LoadXml(xmlLine); return xml; } } }
4.5.
Выводы по использованию классов сериализации
Все классы сериализации библиотеки .NET Framework имеют свои особенности и ограничения, что может вызвать значительные изменения в программном коде при переходе с одной промежуточной среды на другую. Один из способов борьбы с этой проблемой состоит в отказе от сериализации нетривиальных классов (содержащих что-либо, кроме примитивных типов-значений и строк), и особенно сложных списочных структур. Вместо них, вероятно, следует использовать наборы данных 71
Тема 4. Сериализация объектов. Способы сериализации в .NET Framework (класс System.Data.Dataset) или документы XML (класс System.Xml.XMLDocument). Хотя такой способ может являться не совсем удобным для разработчика, он дает залог создания независимого от класса форматирования программного кода.
72
Тема 5. MSMQ – промежуточная среда обмена сообщениями
Тема 5. Microsoft Message Queuing (MSMQ) – промежуточная среда обмена сообщениями 5.1.
Служба обмена сообщениями MSMQ
В настоящий момент существует несколько основных разработок в области промежуточного программного обеспечения для работы с очередями сообщений. Наиболее известными разработками являются такие системы очередей сообщений, как MSMQ, Sun Java System Message Queue, IBM MQSeries, Oracle Advanced Queing. Промежуточная среда MSMQ – разработка Microsoft для асинхронной передачи сообщений внутри локальной сети, впервые появившаяся в составе операционной системы Windows NT. В настоящее время последней является версия MSMQ 3.0, включенная в Windows XP PE и 2003 Server, достаточно актуальна так же версия 2.0, включенная в состав операционной системы Windows 2000. Служба MSMQ позволяет произвольному набору приложений добавлять сообщения в некоторую очередь, и произвольному же набору – читать сообщения из очереди. Приложению, использующему MSMQ, доступны следующие основные операции: –
добавить сообщение в очередь;
–
извлечь первое сообщение из очереди;
–
установить обработчик, вызываемый при появлении сообщения.
Структура сообщения определяется приложением, и может быть произвольной, с ограничением на размер одного сообщения (2Мб для MSMQ 2.0). Далее будет рассмотрено
использование
MSMQ
2.0/3.0
при
разработке
приложений
с
использованием .NET Framework.
73
Тема 5. MSMQ – промежуточная среда обмена сообщениями При отправке сообщения с использованием MSMQ посылающему приложению необходимо указать имя компьютера и очереди, в которую его необходимо доставить. После вызова приложением функции отправки сообщение сохраняется в локальной исходящей очереди. Затем MSMQ определяет имя компьютера и очереди, куда необходимо передать сообщение. Возможны следующие случаи: –
сообщение доставляется сразу в указанную отправителем очередь (прямая доставка);
–
сообщение посылается в некоторую промежуточную очередь, определяемую маршрутизатором службы сообщений;
–
MSMQ определяет, что сообщение требуется разослать в несколько очередей (возможность поддерживается начиная с MSMQ 3.0). Компьютер с рассылающим сообщения приложением Приложение
Компьютер с промежуточной очередью сообщений MSMQ
MSMQ Исходящая очередь
Входящая очередь
Исходящая очередь
MSMQ MSMQ
Исходящая очередь
Входящая очередь
Приложение Компьютер с читающим сообщения приложением
Компьютер с очередью – пунктом назначения сообщений
Рисунок 5.1. Пересылка сообщения в промежуточной среде MSMQ После определения имени компьютера с очередью назначения, MSMQ проверяет доступность компьютера (пакетом UDP) и в случае ответа сразу пытается отправить ему сообщение, повторяя попытки с интервалом по умолчанию 5 секунд.
Если
сообщение не удается отправить, то обычно каждые 5 минут служба сообщений 74
Тема 5. MSMQ – промежуточная среда обмена сообщениями пытается найти новый пункт назначения сообщения, используя маршрутизацию MSMQ. Процесс пересылки сообщения между компьютерами повторяется, пока оно не достигнет очереди назначения. С момента поступления сообщения в указанную при отправке очередь любое использующее MSMQ приложение с необходимыми правами доступа может прочитать это сообщение (рис 5.1). Таким образом, MSMQ поддерживает асинхронную передачу сообщений, при которой участвующие в обмене компоненты распределенной системы не обязательно должны функционировать в один и тот же момент времени. Благодаря службе маршрутизации сообщений возможно создание моста между MSMQ и аналогичной технологией IBM – IBM Websphere MQ (ранее MQSeries). Websphere MQ может использоваться и напрямую программами .NET Framework, однако обычно это менее удобно, чем использование MSMQ, и может быть связано с дополнительными затратами – служба MSMQ уже входит в большинство систем семейства Windows. Использование моста MSMQ-MQSeries позволяет создавать гетерогенные распределенные системы на основе обмена сообщениями, поскольку технология IBM MQ является изначально межплатформенной. Приложение может вести поиск нужной ему очереди по ряду критериев. Это возможно при использовании механизма общих очередей в Microsoft Message Queuing, что требует развертывания Microsoft Active Directory. 5.2.
Инфраструктура, необходимая для использования MSMQ
Служба MSMQ может работать как в составе домена Active Directory, так и при отсутствии такого домена, но во втором случае невозможно использовать ряд возможностей MSMQ, а именно: –
не поддерживается шифрование передаваемых сообщений;
–
не поддерживаются общие очереди и механизмы их обнаружения;
–
не поддерживается маршрутизация сообщений и групповая рассылка сообщений.
75
Тема 5. MSMQ – промежуточная среда обмена сообщениями В MSMQ существуют два вида очередей – общие (public) и частные (private). Как частные, так и общие очереди могут либо использовать, либо не использовать транзакции, что задается при создании очереди и не может быть изменено в дальнейшем. Информация об общих очередях публикуется в службе каталогов Active Directory. Путь к общей очереди имеет вид ComputerName\QueueName, возможна также запись пути в виде .\QueueName для очереди на локальном компьютере. Посылающее сообщение приложение может искать доступные в домене общие очереди по заданным критериям. Возможна также проверка приложением наличия общей очереди на удаленном компьютере
и
ее
создание.
Для
использования
общих
очередей
требуется
развертывание MSMQ в рамках домена Active Directory. Частные очереди доступны как при наличии домена, так и при его отсутствии. Путь к такой очереди имеет вид ComputerName\Private$\QueueName или .\Private$\QueueName для локального компьютера. Приложение, не имеет возможности ни создать, ни проверить наличие частной очереди на удаленном компьютере при использовании средств библиотеки FCL. В MSMQ 3.0 появилась возможность посылать сообщения в общую или частную очередь по протоколу HTTP при наличии на удаленном компьютере службы Internet Information Services (IIS). В этом случае используется путь к очереди вида http://domain.name/msmq/private$/queuename. Посылающий сообщения компьютер так же должен поддерживать HTTP для MSMQ, иначе он не сможет получать какие-либо ответы.
При
чтении
сообщений
использовать
HTTP-формат
имени
очереди
невозможно. MSMQ поддерживает два вида имени очереди – прямой вид (direct name) и путь к очереди (path name). При использовании частных очередей на удаленном компьютере обычно необходимо использовать прямой путь к очереди. При использовании MSMQ программами на .NET Framework прямой путь записывается следующим образом: –
Formatname:DIRECT=OS:server01\QueueName – прямое имя общей очереди на компьютере server01; 76
Тема 5. MSMQ – промежуточная среда обмена сообщениями –
Formatname:DIRECT=OS:ws02\Private$\QueueName – прямое имя частной очереди на компьютере ws01;
–
Formatname:DIRECT=TCP:192.168.30.1\private$\QueueName –
прямое имя
частной очереди на компьютере с адресом 192.168.30.1. Полноценная работа с очередями возможна только в пределах локальной или виртуально частной сети, поскольку при чтении сообщений используется Windows RPC (в частности, обращения на 135-й порт). Кроме этого, среда MSMQ не способна функционировать при использовании NAT, за исключением случая HTTP-имени очереди. 5.3.
Применение службы сообщений MSMQ в распределенных системах
Рассмотрим возможности, предоставляемые службой MSMQ как промежуточным программным
обеспечением
с
точки
зрения
требований,
предъявляемых
к
распределенной системе. 1. Открытость. Требование выполняется частично: используя в качестве сообщения XML-документ с заданной XSD-схемой, можно добиться открытости передаваемых данных. Однако сама технология MSMQ является закрытой и одноплатформенной. 2. Безопасность. MSMQ поддерживает обычные для Windows NT списки управлением доступа (ACL) для каждой очереди, а при наличии домена AD – прозрачное шифрование сообщений. Таким образом, система, использующая MSMQ, может являться безопасной при развертывании в пределах домена Active Directory. 3. Расширяемость. MSMQ позволяет создавать масштабирумые системы, поскольку не накладывает ограничений на число машин, считывающих сообщения из одной очереди. Если время обработки сообщения удаленным приложением несравнимо больше времени на его пересылку между очередями, то возможен линейный рост производительности
при
увеличении
числа
компьютеров,
считывающих
и
обрабатывающих сообщения из очереди.
77
Тема 5. MSMQ – промежуточная среда обмена сообщениями 4. Обеспечение целостности данных. MSMQ поддерживает внутренние транзакции, распределенные транзакции среды COM+ и транзакции из System.Transactions. Внутренние транзакции гарантируют, что некоторая последовательность операций компоненты с очередями (например, получение сообщение и отправка ответа на него) будет либо выполнена полностью, либо не выполнена вообще. Транзакции среды COM+
и
пространства
имен
System.Transactions
используют
координатор
распределенных транзакций (MS DTC). При их использовании в последовательность операций, образующих транзакцию, кроме действий с очередями сообщений могут входить операции с любыми службами, поддерживающие распределенные транзакции. Кроме использования транзакций, для повышения надежности в MSMQ следует использовать механизм восстанавливаемых сообщений, который повышает вероятность того, что после принятия службой MSMQ сообщение не будет потеряно при аварийном отключении питания. Распределенные транзакции COM+ будут рассмотрены в следующем разделе. 5. Устойчивость.
Основной
особенностью
MSMQ
является
возможность
использования нескольких читающих из очереди серверов. Кроме того, при использовании общих очередей возможен выбор того или иного компьютера с требуемой очередью. В силу этого при использовании очередей сообщений можно избежать возникновения уникальной точки уязвимости. Таким образом, с точки зрения требований к распределенной системе технология MSMQ обладает многочисленными достоинствами. Однако данная технология промежуточного ПО также имеет ряд важных недостатков. 1. Использование сообщений происходит непрозрачно для приложения, в отличие от использования механизмов удаленного вызова. 2. MSMQ неудобно использовать вне LAN/VPN. Если какой-либо компоненте нужно посылать сообщения по протоколу HTTP и при этом получать ответы с результатом обработки своих сообщений, то вероятно предпочтительнее либо использовать VPN, либо рассмотреть вариант использования синхронных удаленных вызовов.
78
Тема 5. MSMQ – промежуточная среда обмена сообщениями 3. Для реализации модели «запрос-ответ» при использовании MSMQ предпочтительно применять отдельную очередь ответов для каждой посылающей запросы компоненты. Использование очередей, поддерживающих транзакции, отличается от использования очередей без поддержки транзакций, причем использование внутренних и внешних транзакций заметно отличается. Работа с частными очередями может отличаться от работы с общими очередями. В свою очередь набор возможных операций с частными локальных очередями (.\Private$\LocalQueueName) отличается от такового для удаленных частных очередей (SomeComputer\Private$\RemoteQueueName). Поэтому при использовании MSMQ, как и других промежуточных сред, рекомендуется создать промежуточный слой взаимодействия с ним. Такой слой скрывал бы использование очередей от вышестоящих компонент и позволял безболезненно перейти от частных к общим очередям или наоборот, от внутренних транзакций ко внешним и упростил бы переход на другое промежуточное программное обеспечение. Службы очередей сообщений находят наилучшее применение в случаях, когда какая-либо компонента распределенной системы посылает заявки, не требующие ответа (модель «запрос»). Если же компонента получает некоторые ответы в асинхронном режиме (модель «запрос-ответ»), то она должна, вероятно, поддерживать собственный список ожидающих ответов заявок. Это означает наличие у компоненты некоторого внутреннего состояния, которое должно сохраняться в течение длительного времени. Наличие такого состояния является недостатком с точки зрения концепции распределенной системы как набора сервисов, не сохраняющих историю своих вызовов. С точки зрения этой концепции использующая MSMQ компонента либо дожидается ответа сервера в течение одного своего вызова другими компонентами, либо не нуждается в ответе вообще. В первом случае MSMQ предпочтительнее технологий удаленного вызова в одном из в следующих случаев: –
обеим
взаимодействующим
компонентам
известен
только
посредник
(компьютер с используемой очередью сообщений); –
заявки обрабатываются несколькими компьютерами параллельно;
–
вызывающая компонента не знает внешний интерфейс удаленного вызова вызываемой компоненты. 79
Тема 5. MSMQ – промежуточная среда обмена сообщениями Наличие хороших возможностей масштабирования у систем, использующих очереди сообщений, может быть одним из доводов в пользу выбора такой технологии в качестве промежуточной среды. В частности, одним из возможных приложений MSMQ является создание вычислительных распределенных приложений, реализующих какие-либо численные методы, поскольку в настоящий момент MSMQ является практически единственным доступным при использовании .NET Framework средством обмена сообщениями, которое может быть использовано для разработки параллельных вычислительных приложений. Поскольку накладные расходы на использование очередей сообщений достаточно существенны, то использование очередей сообщений оправдано в тех случаях, когда выполняемая на удаленном компьютере задача выполняется значительное время (порядка десятых долей секунды и более). Отдельным вопрос при использовании службы MSMQ является решение такой проблемы, как единственность используемой очереди. Поскольку очередь находится на каком-либо заданном компьютере, она является узким местом с точки зрения устойчивости и расширяемости системы. К сожалению, при использовании MSMQ из .NET Framework нет возможности узнать, например, среднюю длину или время нахождения заявок в очереди для выбора приложением одной из нескольких очередей. Для увеличения устойчивости системы можно было бы выбирать работающую очередь из нескольких возможных, но для проверки готовности компьютера с удаленной очередью видимо скорее имеет смысл реализовать посылку тестового пакета ICMP, чем дожидаться от MSMQ известия о невозможности послать сообщение в очередь. Таким образом, решение вопроса балансировки нагрузки на менеджеры очередей и дублирование их функций в настоящий момент остается за разработчиком распределенной системы. 5.4.
Использование очередей сообщений MSMQ в .NET Framework
Для работы с очередями сообщений используются классы из пространства имен System.Messaging. Класс System.Messaging.MessageQueue содержит три группы методов. 1. Статические методы для администрирования очередей: Create, Delete, Exists, Purge. 80
Тема 5. MSMQ – промежуточная среда обмена сообщениями 2. Методы поиска общих очередей: GetPublicQueues, GetPublicQueuesByLabel и другие. При их использовании можно создать приложение, которое переключается между несколькими менеджерами очередей в пределах Active Directory, если один из них выходит из строя. 3. Методы для работы с сообщениями (Send, Receive, Peek и другие), в том числе позволяющие
использовать
обработчик
на
завершение
операции
(BeginPeek,
BeginReceive). При применении классов из System.Messaging возможно три варианта работы с очередями сообщений: –
работа с очередями, не использующими транзакции;
–
работа с очередями, поддерживающими транзакции, при использовании внутренних транзакций MSMQ;
–
работа с очередями, поддерживающими транзакции, при использовании распределенных транзакций COM+.
Определенной трудностью при использовании MSMQ в .NET Framework являются различия по использованию разных видов очередей. В частности, для очередей без транзакций можно установить обработчик на завершение приема сообщения, а для очередей с транзакциями этот способ неприемлем, поскольку само чтение сообщения должно быть оформлено как часть транзакции. Для обоих видов очередей можно поставить обработчик на появление сообщения в очереди, что и является рекомендованным способом. Для сериализации и десериализации сообщений MSMQ могут использоваться классы XMLMessageFormatter System.Messaging.
или Класс
BinaryMessageFormatter XMLMessageFormatter
из
пространства использует
имен класс
System.Xml.Serialization.XmlSerializer, описанный ранее в теме о сериализации, поэтому
при
использовании
XMLMessageFormatter
должны
учитываться
все
особенности использования класса XmlSerializer. Класс BinaryMessageFormatter аналогичным способом использует для сериализации класс BinaryFormatter.
81
Тема 5. MSMQ – промежуточная среда обмена сообщениями Поскольку классы BinaryFormatter и XmlSerializer имеют различные ограничения на сериализуемые классы и используют совершенно различные процедуры сериализации, в нетривиальном случае переход BinaryMessageFormatter на XMLMessageFormatter или наоборот может привести к определенным изменениям в исходном коде программных компонент.
Рекомендованным
XMLMessageFormatter.
Его
для
использования
применение
позволяет
с
MSMQ создать
следует
считать
XSD-схему
для
передаваемого сообщения. При использовании MSMQ ни значительно меньший объем сообщения, создаваемого классом BinaryMessageFormatter, ни его меньшее время работы не является принципиальными факторами. Ниже рассмотрено вспомогательное пространство имен с классами общего вида, реализующими модель «запрос-ответ» при использовании внутренних транзакций MSMQ (рис 5.2).
Клиенты
Запросы MSMQ
Серверы
Ответы
Рисунок 5.2. Обслуживание запросов клиентов при использовании MSMQ Программа
использует
пространство
имен
с
классами
передачи
сообщений
System.Messaging и пространство имен с коллекциями общего вида. using System; using System.Messaging; using System.Collections.Generic;
Классы используют два делегата общего вида, которые будут связаны с событиям обработки сообщения сервером и получения ответа клиентом. namespace Seva.Msmq { // типы очередей enum QueueType {NonTransactional, Transactional}; // типы классов форматирования enum QueueFormatter {Binary, Xml};
82
Тема 5. MSMQ – промежуточная среда обмена сообщениями // делегат общего вида для обработки сервером сообщений клиента delegate AnswerType ProcessRequestEventHandler (Object sender, RequestType request, MessageQueue queueResponse); // делегат общего вида для обработки ответов сервера клиентом delegate void ProcessAnswerEventHandler (Object sender, RequestType request, AnswerType answer);
Абстрактный класс MSMQUser, наследуемый классами MSMQServer и MSMQClient. public abstract class MsmqUser { // использование восстанавливаемых сообщений private bool recoverable = false; public bool Recoverable { get { return recoverable; } set { recoverable = value; } } // объекты форматирования для посылки приема сообщений protected IMessageFormatter requestFormatter; protected IMessageFormatter answerFormatter; // public MsmqUser(QueueFormatter formatterType) { if (formatterType == QueueFormatter.Xml) { requestFormatter = new XmlMessageFormatter( new Type[]{typeof(RequestType)}); answerFormatter = new XmlMessageFormatter( new Type[]{typeof(AnswerType)}); } if (formatterType == QueueFormatter.Binary) { requestFormatter = new BinaryMessageFormatter(); answerFormatter = new BinaryMessageFormatter(); } } }
Класс общего вида, посылающий через MSMQ запросы и получающий ответы на них. class MsmqClient : MsmqUser, IDisposable { // очереди для отсылки запросов и приема ответов private MessageQueue queueSend; private MessageQueue queueReceive; // список необслуженных запросов private Dictionary messages; public Dictionary Messages { get { return messages;} }
83
Тема 5. MSMQ – промежуточная среда обмена сообщениями // событие, вызываемое при приеме ответа public event ProcessAnswerEventHandler ProcessAnswer;
Конструктор, получающий имена очередей для посылки и приема сообщений. public MsmqClient(String queueSendName, String queueReceiveName, QueueFormatter formatterType): base(formatterType) { // список отправленных сообщений без ответов messages = new Dictionary(); // создание очереди для посылки запросов, если она не существует queueSend = MsmqTools.CreateQueue(queueSendName, QueueType.Transactional); // создание очереди для приема ответов, если она нужна if (queueReceiveName != null) { queueReceive = MsmqTools.CreateQueue(queueReceiveName); queueReceive.Formatter = answerFormatter; // считывать из очереди свойство CorrelationId queueReceive.MessageReadPropertyFilter.CorrelationId = true; } else { queueReceive = null; } }
В методе Dispose происходит закрытие используемых очередей. public void Dispose() { queueSend.Close(); queueSend.Dispose(); if (queueReceive != null) { queueReceive.Close(); queueReceive.Dispose(); } }
Функции BeginReceive и EndReceive начинают и прекращают прием ответов сервера, изменяя обработчик события PeekComplete очереди ответов. public void BeginReceive() { // установить обработчик на событие, возникающее при появлении // сообщения в очереди queueReceive.PeekCompleted += OnPeek; // начать отслеживание поступления сообщения в очередь queueReceive.BeginPeek(); }
84
Тема 5. MSMQ – промежуточная среда обмена сообщениями // прекратить прием ответов сервера public void EndReceive() { // отключить обработчик queueReceive.PeekCompleted -= OnPeek; }
Функция Send посылает в исходящую очередь запрос общего типа для его обработки сервером. Для ответа на сообщение серверу следует использовать очередь, указанную в поле ResponseQueue посылаемого сообщения. public void Send(RequestType request) { // создание нового сообщения Message message = new Message(request, requestFormatter); message.ResponseQueue = queueReceive; // использование восстаналиваемых сообщений message.Recoverable = Recoverable; // послать сообщение; поскольку транзакция состоит из // единственной операции, вместо объекта-транзакции используется // значение MessageQueueTransactionType.Single queueSend.Send(message, MessageQueueTransactionType.Single); // поле message.Id устанавливается после посылки сообщения; // идентификатор сообщения связывается c отосланным запросом // в списке необслуженных запросов messages.Add(message.Id, request); }
Обработчик события очереди PeekComplete использует внутренние транзакции MSMQ. В одну транзакцию входит операция чтения ответа из очереди и последующий вызов события ProcessAnswer. Если в ходе обработки события возникло исключение, ответ сервера останется в очереди ответов. Иначе сообщение удаляется из поддерживаемого клиентом списка невыполненных запросов. public void OnPeek(Object source, PeekCompletedEventArgs asyncResult) { // создание внутренней транзакции MSMQ MessageQueueTransaction transaction = new MessageQueueTransaction(); // начало транзакции transaction.Begin(); try { // прекратить ожидание сообщений в очереди queueReceive.EndPeek(asyncResult.AsyncResult); // получить сообщение из очереди в рамках транзакции Message message = queueReceive.Receive(transaction);
85
Тема 5. MSMQ – промежуточная среда обмена сообщениями // в поле CorrelationId должен быть идентификатор сообщения // с исходным запросом String messageId = message.CorrelationId; // есть ли такое сообщение в списке невыполненных запросов? if (messages.ContainsKey(messageId)) { if (message.Body is AnswerType) { // преобразовать тело сообщения к типу ответа // и вызвать событие по его обработке AnswerType answer = (AnswerType) message.Body; ProcessAnswer(this, messages[messageId], answer); }; messages.Remove(messageId); } // продолжить ожидать сообщения BeginReceive(); // успешное завершение транзакции transaction.Commit(); } catch (Exception e) { // отмена транзакции transaction.Abort(); throw e; } } }
MSMQServer – класс общего вида, принимающий через MSMQ запросы и посылающий ответы на них. class MsmqServer: MsmqUser, IDisposable { // очередь приема запросов private MessageQueue queueReceive; // событие, вызываемое при приеме запроса public event ProcessRequestEventHandler ProcessMessage;
Конструктор класса проверяет наличие очереди. public MsmqServer(String queueReceiveName, QueueFormatter formatterType): base(formatterType) { // создание очереди приема сообщений, если она не существует queueReceive = MsmqTools.CreateQueue(queueReceiveName, QueueType.Transactional); queueReceive.Formatter = requestFormatter; }
В методе Dispose происходит закрытие используемых очередей.
86
Тема 5. MSMQ – промежуточная среда обмена сообщениями public void Dispose() { queueReceive.Close(); queueReceive.Dispose(); }
Функции BeginReceive и EndReceive начинают и прекращают прием ответов сервера, изменяя обработчик события PeekComplete очереди ответов. // начать прием запросов от клиента public void BeginReceive() { queueReceive.PeekCompleted += OnPeek; queueReceive.BeginPeek(); } // прекратить прием запросов от клиента public void EndReceive() { queueReceive.PeekCompleted -= OnPeek; }
Метод OnPeek – обработчик события PeekCompleted очереди с запросами. В одну транзакцию входит две операции с очередями – чтения запроса и отправка ответа на него. Для обработки принятого сообщения и создания ответа на него вызывается событие ProcessMessage. В поле ResponseQueue полученного сообщения содержится ссылка на очередь, в которую следует отправить ответ на обработанный запрос. // обработчки события PeekCompleted очереди с запосами public void OnPeek(Object source, PeekCompletedEventArgs asyncResult) { // создание внутренней транзакции MSMQ MessageQueueTransaction transaction = new MessageQueueTransaction(); // начало транзакции transaction.Begin(); try { queueReceive.EndPeek(asyncResult.AsyncResult); // прием cообщения в рамках транзакции Message message = queueReceive.Receive(transaction); // в поле ResponseQueue содержится ссылка на очередь, // куда следует послать ответ на запрос MessageQueue queueResponse = message.ResponseQueue; try { if (message.Body is RequestType) { RequestType request = (RequestType) message.Body; // вызвать событие обработки запроса AnswerType answer = ProcessMessage(this, request, queueResponse);
87
Тема 5. MSMQ – промежуточная среда обмена сообщениями if ((queueResponse != null) && (answer != null)) { Message answerMessage = new Message(answer, answerFormatter); answerMessage.Label = "Answer"; answerMessage.CorrelationId = message.Id; answerMessage.Recoverable = Recoverable; // послать собщение в рамках транзакции queueResponse.Send(answerMessage, transaction); } } } finally { if (queueResponse != null) { queueResponse.Close(); queueResponse.Dispose(); } }; // продолжить прием запросов BeginReceive(); // завершить транзакцию transaction.Commit(); } catch (Exception e) { // отменить транзакцию в случае ошибки Console.WriteLine(e); transaction.Abort(); throw e; } } }
Класс MsmqTools содержит вспомогательный статический метод для создания очереди сообщений. static class MsmqTools { static public MessageQueue CreateQueue(String queueName) { return CreateQueue(queueName, QueueType.Transactional); } // функция проверяет наличие очереди и создает ее при необходимости static public MessageQueue CreateQueue(String queueName, QueueType type) { MessageQueue messageQueue;
88
Тема 5. MSMQ – промежуточная среда обмена сообщениями
}
// если это частная очередь удаленного компьютера, // то при попытке проверки ее наличие возникает исключение try { if (!MessageQueue.Exists(queueName)) { MessageQueue.Create(queueName, type == QueueType.Transactional); } } catch(Exception) { } MessageQueue messageQueue = new MessageQueue(queueName); return messageQueue;
} }
Следует отметить, что при работе с общими очередями можно обращаться к очереди по ее пути, например следующим образом. queueName = @"Server\PublicQueue";
При использовании частных очередей на удаленном компьютере в большинстве случаев требуется применять прямое имя очереди. queueName = @"Formatname:DIRECT=OS:Computer\Private$\PrivateName";
Имена используемых
очередей следует хранить
в конфигурационном
файле
программы. 5.5.
Выводы по использованию MSMQ
Промежуточная среда Microsoft Message Queuing обеспечивает асинхронный обмен сообщениями
и
может
быть
использована
программными
компонентами
распределенной системы в одном из следующих случаях: –
необходимо
организовать
параллельную
обработку
заявок
несколькими
компьютерами; –
одна компонента посылает другой запросы без получения ответов на них;
–
взаимодействие компонент не должно быть синхронным;
–
требуется интеграция с какими-либо другими независимыми
системами,
которые могут использовать очереди сообщений (MSMQ или IBM MQ). 89
Тема 5. MSMQ – промежуточная среда обмена сообщениями Альтернативным способом использования MSMQ являются отложенные компоненты (queued components) среды COM+, которые будут рассмотрены в разделе, посвященном COM+. При использовании отложенных компонент MSMQ теряет одно из своих достоинств – клиент должен иметь доступ к интерфейсу удаленной компоненты, как и в случае использования любых других COM+ компонент. Кроме того, существует возможность использовать MSMQ как канал в .NET Remoting, для чего необходимо создать собственный канал, что будет проделано в соответствующей теме. Таким образом, MSMQ является не только самостоятельной промежуточной средой, но и может быть использовано другими промежуточными средами как асинхронный канал передачи данных.
90
Тема 6. Сервисы распределенных систем COM+
Тема 6. Промежуточная среда COM+ и служба Enterprise Services 6.1.
Введение в промежуточную среду COM+
COM+ – промежуточная среда для создания распределенных систем, действующих в локальной сети. Она разрабатывается фирмой Microsoft с конца 90-х годов и впервые появилась в составе операционной системы Microsoft Windows 2000. Основной целью разработки
среды
COM+
было
создание
инфраструктуры
для
разработки
распределенных систем автоматизации предприятия. Основные достоинства среды COM+: –
поддержка как синхронного, так и асинхронного взаимодействия программных компонент;
–
совместная работа с координатором распределенных транзакций (distributed transactions coordinator, DTC);
–
поддержка метода доступа единственного вызова с пулом объектов;
–
использование
для
ограничения
доступа
к
компоненте
ролей
(roles),
связываемых администратором системы с учетными записями пользователей. Среда
COM+
управляет
ходом
выполнения
объектов
COM+,
являющимися
экземплярами так называемых компонент COM+. Набор связанных компонент COM+, находящихся в одной динамической библиотеке, называется приложением COM+. Приложение COM+ состоит из набора компонент и ролей для доступа к ним. Сведения о зарегистрированных приложениях хранятся в каталоге COM+. Приложения COM+ бывают двух видов: библиотечные и серверные. Экземпляры компонент библиотечных приложений выполняются в том же процессе, что и использующий их клиент, компоненты серверного – в отдельном потоке сервера, возможно выполняющимся на удаленном компьютере. Только серверные приложения могут использоваться удаленно путем регистрации в каталоге COM+ на компьютере клиента посредника приложения COM+. После установки посредников использование удаленных серверных компонент COM+ не отличается от использования локальных 91
Тема 6. Сервисы распределенных систем COM+ серверных компонент. Понятие посредник (proxy) используется в COM+ в двух различных смыслах – запись в каталоге COM+, связанная с некоторым приложением COM+ на удаленном компьютере, и в том же смысле, что и посредник при удаленном вызове. Поскольку библиотечные приложения COM+ не используют механизм удаленных вызовов и могут использоваться только в адресном пространстве клиента, то их рассмотрение не относится к данному курсу, и далее речь пойдет исключительно о серверных приложениях COM+. На рисунке рис. 6.1 показана архитектура среды COM+ при использовании серверных приложений.
Объекты
COM+
являются
экземплярами
компонент
COM+,
зарегистрированных в каталоге. Заглушка на стороне серверного процесса называется в COM+ перехватчиком (interceptor).
Объект COM+
Среда COM+
Серверный процесс
Объект COM+
Перехватчик
Каталог COM+
Перехватчик
RPC
Объект COM+
Посредник
Посредник
RPC
Перехватчик
Вызывающая функция
Каталог COM+
Клиентский процесс Компьютер 1
Среда COM+ Компьютер 2
Рисунок 6.1. Архитектура среды COM+ Каталог COM+ каждого компьютера содержит список зарегистрированных на компьютере
локальных
приложений
COM+,
а
также
список
установленных 92
Тема 6. Сервисы распределенных систем COM+ посредников для связи с приложениями удаленных компьютеров. Каталог устроен иерархически, в виде дерева. Например, узел с описанием приложения COM+ содержит узел со списком входящих в него компонент COM+ и узел со списком ролей приложений. Управление каталогом COM+
происходит при помощи оснастки
comexp.msc или программно, используя классы библиотеки comadmin.dll. Подробнее состав каталога описан в приложении I. Поскольку среда COM+ реализует свое собственное управлением исполняемым внутри нее кодом, то существует понятие контекста COM+, который представляет собой окружение объекта COM+. Контекст закрепляется за объектом в момент его активации и сохраняется до его деактивации. При создании контекста учитываются атрибуты необходимости
транзакции
и
синхронизации,
установленные
в
активируемой
компоненте COM+, а так же текущий контекст в момент активации объекта. 6.2.
Сервисы COM+
Целью создания промежуточной среды COM+ являлось предоставление компонентам COM+ набора сервисов, облегчающему создание надежной и масштабируемой распределенной системы. Начиная с Windows 2003/XP SP2, часть этих сервисов может быть доступна и без создания компонент COM+, путем использования так называемых сервисов без компонент. Синхронизация Среда COM+ позволяет исключить проблемы с синхронизацией при обслуживании запроса клиента путем так называемых активностей. Активность начинается в момент создания клиентом COM+ объекта и заканчивается при его удалении. В течении активности клиент вызывает методы компоненты COM+, в ходе которых она может создавать другие объекты COM+, в том числе расположенные на удаленных компьютерах, и вызывать их методы. При этом COM+ гарантирует, что в течение одной активности в каждый момент выполняется метод только одного COM+ объекта из всех участвующих в активности. Таким образом, активность представляет собой некий логический поток. Объекты COM+ могут как участвовать в активности (требовать синхронизации), так и не участвовать, в зависимости от атрибутов 93
Тема 6. Сервисы распределенных систем COM+ соответствующей им компоненты COM+ и текущего контекста при их создании, как показано в таблице 6.1. Таблица 6.1 Синхронизация создаваемого объекта COM+ Настройка синхронизации компоненты COM+
Создатель объекта участвует в активности
Создатель объекта не участвует в активности
Не поддерживается (Not Supported)
Вне активности
Вне активности
Поддерживается (Supported)
Активность создателя
Вне активности
Требуется (Required)
Активность создателя
Новая активность
Требуется новая (Requires new)
Новая активность
Новая активность
Следует учитывать, что настройки участия в транзакции компоненты влияют на ее настройки синхронизации, о чем будет сказано далее. Балансировка нагрузки COM+ поддерживает динамическую балансировку нагрузки. Для этого необходимо создать кластер COM+ на базе серверной версии операционной системы Microsoft Windows (Windows 2000 Advanced Server и последующих). Кластер COM+ состоит из нескольких серверов и распределяющим между ними запросы маршрутизатором COM+. Для повышения надежность такой системы могут применяться способы быстрой замены маршрутизатора при его выходе из строя на базе Windows Clustering Services. Таким образом, среда COM+ позволяет при наличии необходимости достаточных финансовых ресурсов создать хорошо масштабируемую систему без уникальной точки сбоя. Just-in-time-активация и пул объектов Среда COM+ поддерживает два вида активации объектов – активация по требованию клиента и активация на время единственного вызова (называемая в среде COM+ JIT-активацией)
с
возможностью
использования
пула
объектов.
Поскольку 94
Тема 6. Сервисы распределенных систем COM+ распределенные транзакции поддерживаются только для второго типа активации, в дальнейшем будет рассматриваться только он. Распределенные транзакции Одним из основных достоинств среды COM+ является поддержка распределенных транзакций на базе координатора распределенных транзакций. Настройки транзакции компоненты COM+ влияют на настройки синхронизации и активации. Любая включенная настройка транзакций, отличная от Not supported, требует использования JIT-активации и ограничивает выбор настройки синхронизации не более чем вариантами Required и Requires new. При активации объекта среда COM+ определяет необходимость использования транзакций в соответствии с таблицей 6.2 Таблица 6.2 Участие объекта COM+ в транзакции Настройка транзакции компоненты COM+
Создатель объекта участвует в транзакции
Создатель объекта не участвует в транзакции
Не поддерживается (Not Supported)
Вне транзакции
Вне транзакции
Поддерживается (Supported)
Транзакция создателя
Вне транзакции
Требуется (Required)
Транзакция создателя
Новая транзакция
Требуется новая (Requires new)
Новая транзакция
Новая транзакция
Если при создании объекта обнаружена потребность в создании новой транзакции, среда COM+ создает ее с помощью координатора транзакций и данный объект считается корнем транзакции (рис. 6.2). Поскольку требующие транзакцию объекты используют активацию одного вызова, то транзакция не может существовать дольше, чем один вызов метода корневого объекта клиентом COM+. В транзакции могут участвовать службы, имеющие свой менеджер ресурсов, в частности MSMQ и MS SQL. Для многих ресурсов при необходимости можно создать свой компенсирующий менеджер ресурсов.
95
Тема 6. Сервисы распределенных систем COM+ Кординатор транзакций
Контекст COM+
Клиент
Корневой объект транзакции
ID транзакции
ID транзакции Контекст COM+
БД
Объект, участвующий в транзакции
Компенсирующий менеджер ресурсов
Участники транзакции
Рисунок 6.2. Транзакция COM+ Каждый из участвующих в транзакции объектов должен в конце выполнения своего метода сообщить об успешности транзакции или ее неудаче. В случае, если все объекты объявили об успешности транзакции, служба COM+ оповещает все участвующие в транзакции внешние службы о необходимости сделать произведенные в рамках транзакции изменения постоянными (рис. 6.3). Корневой объект транзакции
Успех
Компенсирующий менеджер ресурсов
Сохранить изменения
Успех
Объект, участвующий в т ранзакции
БД Сохранить изменения
Клиент
Координатор транзакций
Рисунок 6.3. Успешное завершение транзакции COM+
96
Тема 6. Сервисы распределенных систем COM+ Ожидающие компоненты Хотя среда MSMQ может использоваться в рамках транзакции COM+, это приводит к одновременному
использованию
двух
технологий
удаленного
взаимодействия.
Вероятно часто было бы удобно скрывать использование MSMQ путем создания компонент COM+, поддерживающих асинхронные коммуникации. Поэтому для реализации асинхронного удаленного вызова в промежуточной среде COM+ существуют так называемые ожидающие компоненты (queued componenets), которые прозрачно
используют
MSMQ.
Использование
такой
компоненты
подобно
асинхронному удаленному вызову (рис. 6.4).
Клиент
Каталог COM+
Интерфейс компоненты
Компьютер клиента отложенной компоненты
Протоколист MSMQ
Компьютер с отложенной компонентой
Слушатель Помощник слушателя Исполнитель Интерфейс компоненты Отложенная компонента COM+
Рисунок 6.4. Использование ожидающих компонент COM+ 1. При начале использования ожидающей компоненты на стороне клиента создается посредник, называемый протоколистом (recorder), сохраняющий историю вызовов компоненты.
97
Тема 6. Сервисы распределенных систем COM+ 2. После завершения использования компоненты, если не произошло отката транзакции,
протоколист
формирует
сообщение
MSMQ
со
всеми
вызовами
компоненты. 3. На стороне сервера сообщение MSMQ ожидается слушателем (listener), который не является COM+ компонентой. При появлении сообщения в очереди он создает специальную COM+ компоненту, называемую помощником слушателя (listener helper), которая считывает сообщение из очереди. 4. После считывания сообщения помощник слушателя создает исполнитель (player), который и создает сам экземпляр отложенной компоненты, воспроизводя затем последовательность ее вызовов в рамках той же транзакции. С точки зрения отложенной компоненты исполнитель является обычным клиентом. Аналогичным
образом
происходит
получение
результата
вызова
удаленной
компоненты: на сервере создается протоколист, а на клиенте – слушатель и исполнитель. Однако в этом случае до начала использования удаленной компоненты клиенту следует создать вызываемый объект (call-back object), который будет принимать ответ от сервера через исполнителя, и передать ссылку на такой объект
Протоколист
Вызываемый объект
Исполнитель
Компьютер клиента отложенной компоненты
Исполнитель
Протоколист
Отложенная компонента
Клиент
MSMQ
(точнее, на его исполнителя), серверу (рис. 6.5).
Компьютер с отложенной компонентой
Рисунок 6.5. Взаимодействие с отложенной компоненты Слабо связанные события Среда COM+ позволяет компонентам
подписаться на события, создаваемые
какими-либо объектами COM+ (издателями событий). При этом подписчики и издатели 98
Тема 6. Сервисы распределенных систем COM+ могут не знать о существовании друг друга, но должны знать о существовании общего интерфейса COM+, который используется для публикации событий. Наравне с отложенными компонентами, слабо связанные события предоставляют второй вариант реализации асинхронного обмена данными в среде COM+. Обеспечение безопасности Определение политики управления доступом для компонент COM+ осуществляется на основе ролей безопасности. Компоненты COM+ должны иметь возможность использовать
встроенные
в
операционную
систему
механизмы
обеспечения
безопасности. Для абстрагирования от конкретных пользователей операционной системы в COM+ введено понятие ролей безопасности. Роль безопасности – это категории пользователей приложения COM+, имеющих определенные права на доступ к компонентам данного приложения, их интерфейсам и методам. Разработчик компоненты COM+ задает роли как некоторые символьные значения и определяет уровни доступа для всех использующихся ролей. При разворачивании
приложения
ролям
ставятся
пользователей и групп Microsoft Windows.
в
соответствие
учетные
записи
Следует отметить, что роли COM+ не
связаны напрямую с ролями CLI, поскольку COM+ существует независимо от .NET Framework. Прежде чем назначать роли группам, необходимо понять политику обеспечения безопасности приложения. В идеале названия ролей должны соответствовать категориям пользователей, которым они будут назначены. Кроме того, с помощью оснастки служб компонентов можно получить описание каждой роли, в котором могут содержаться сведения о том, какие пользователи могут назначаться на данную роль. 6.3.
Использование среды COM+ в приложениях .NET Framework
Взаимодействие среды COM+ и среды CLR Среда COM+ была создана до технологии .NET, поэтому она работает с неуправляемым кодом и не является носителем исполняемой среды CLR. Для 99
Тема 6. Сервисы распределенных систем COM+ использовании сервисов COM+ из .NET Framework необходимо получить возможность использовать управляемый код в контексте COM+. Для решения данной проблемы была создана достаточно сложная схема взаимодействия сред CLR и COM+, основанная на понятии компоненты, использующей сервисы COM+ (serviced component), называемой далее обслуживаемой (средой COM+) компонентой. Такая компонента состоит из объекта управляемого кода, принадлежащему наследованному от System.EnterpriseServices.ServicedComponent классу. Благодаря наследованию от ServicedComponent при исполнении методов этого объекта имеется доступ к контексту COM+. Использование таких компонент упрощенно показано на рис. 6.6. Процесс клиента Управляемый код Клиент
Посредник .NET Remoting
Управляемый код Экземпляр сервисной компоненты
Посредник сервисной компоненты
COM+ Процесс сервера
Неуправляемый код
Рисунок 6.6. Взаимодействие COM+ и CLR при использовании серверных приложений Маршализация параметров методов обслуживаемой компоненты при использовании происходит под управлением службы .NET Remoting (с использованием класса форматирования BinaryFormatter), а среда COM+ используется только для реализации своих сервисов.
100
Тема 6. Сервисы распределенных систем COM+ Как видно из рисунка 6.6, обслуживаемая компонента .NET Framework не является, строго говоря, компонентой COM+. Аналогично и промежуточная среда .NET Enterpsise Services является не новым названием среды COM+, а средством использования сервисов COM+ в управляемом коде. Хотя часто можно упрощенно считать, что обслуживаемые компоненты – это компоненты COM+ на управляемом коде, но при разработке обслуживаемых компонент существуют случаи, когда желательно понимать взаимосвязь CLR и COM+. Например, если при вызове метода управляемой компоненты происходит ошибка в момент выполнения неуправляемого кода среды COM+, то полученная от посредником COM+ информация преобразуется в исключение типа System.Runtime.InteropServices.COMException. В качестве текста сообщения в этом исключении фигурируют ошибки среды COM+, достаточно малопонятные с точки зрения управляемого кода, например такие. Unhandled Exception: System.Runtime.InteropServices.COMException (0x8000FFFF): Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)) Unhandled Exception: System.Runtime.InteropServices.COMException (0x8004D082): Exception from HRESULT: 0x8004D082
При возникновении исключения в управляемом коде обслуживаемой компоненты происходит обычное исключение .NET, которое, тем не менее, должно уметь маршализироваться по значению с помощью .NET Remoting между доменами приложений, иначе вместо него к клиенту придет исключение от инфраструктуры .NET Remoting о невозможности сериализации выброшенного исключения. Создание обслуживаемых компонент Обслуживаемая компонента .NET Framework является объектом класса, наследованным от
System.EnterpriseServices.ServicedComponent,
и
отвечающим
следующим
требованиям: –
класс имеет спецификатор public;
–
класс содержит публичный конструктором без параметров;
–
класс не является абстрактными, статическим, или классом общего вида.
Статические методы в классе компоненты возможны, но они выполняются в контексте клиента, и имеют доступа к контексту COM+ клиента, если таковой существует. 101
Тема 6. Сервисы распределенных систем COM+ Класс сервисной компоненты должен быть объявлен в сборке, которая может быть зарегистрирована в качестве приложения COM+. Такая сборка не должна иметь классов общего вида со спецификатором public, и должна быть подписана. Класс обслуживаемой компоненты может иметь атрибуты из пространства имен System.EnterpriseServices, связанные с транзакцией (TransactionAttribute), активацией (JustInTimeActivationAttribute (SynchronizationAttribute).
Как
и
ObjectPoolingAttribute) упоминалось
ранее,
и
синхронизацией
поддержка
транзакции
автоматически означает JIT-активацию и участие в активности, тем не менее следует перечислять атрибут JIT-активации явно. Рассмотрим код простейшей обслуживаемой компоненты. // Файл SampleComponent.cs using System; using System.EnterpriseServices; [assembly: [assembly: [assembly: [assembly:
ApplicationAccessControl(false)] ApplicationName("Serviced Component Example 01")] Description("Sample of .NET serviced component.")] ApplicationActivation(ActivationOption.Server)]
Эти атрибуты задают свойства сборки, которая затем будет зарегистрирована в качестве приложения COM+. Атрибут ApplicationActivation позволяет выбрать серверный или библиотечный тип приложения. namespace ServicedComponentSample { [JustInTimeActivation] [Transaction(TransactionOption.Required)] [ObjectPooling(Enabled=true, MinPoolSize=5, MaxPoolSize=10)] public class SampleComponent: ServicedComponent {
Объявленный выше класс компоненты, как видно из атрибутов, требует наличия транзакции и использует JIT-активацию с пулом объектов public SampleComponent() { }
Для использования в пуле объект должен быть возвращен в свое первоначальное состояние (деактивизирован). Для поддержки режима активации одного вызова компонента не должна иметь состояния, сохраняемого между вызовами ее методов 102
Тема 6. Сервисы распределенных систем COM+ клиентом, и таким образом часто она не требует каких-либо действий для перевода в первоначальное состояние. При необходимости можно перекрыть виртуальные методы Activate и Deactivate, вызываемые при операциях активации и деактивации, и производить в них сброс состояния объекта. protected override void Activate() { } protected override void Deactivate() { // Здесь при необходимости можно очистить // состояние объекта и освободить ресурсы }
Метод CanBePooled возвращает true, если объект может быть возвращен в пул объектов после деактивации, иначе он уничтожается. protected override bool CanBePooled() { // Подтверждает возможность помещения объекта в пул return true; }
Атрибут
System.EnterpriseServices.AutoCompleteAttribute
применяется
для
автоматического участия методов в транзакции. Если метод завершается нормально, то считается, что он подтвердил успешное завершение транзакции, при возникновении же в методе не пойманного исключения транзакция будет отменена. [AutoComplete] public void Do() { } } } // Файл SampleComponent.cs
Альтернативный AutoComplete вариант использования транзакций заключается в вызове статических методов класса ContextUtil. Этот класс используется для доступа и изменения контекста COM+, связанного с обслуживаемой компонентой. Метод ContextUtil.SetComplete сообщает COM+ об успешном завершении метода, а ContextUtil.SetAbort предопределяет отмену транзакции. Используются они обычно следующим образом. 103
Тема 6. Сервисы распределенных систем COM+ try {
... ContextUtil.SetComplete();
} catch() { ContextUtil.SetAbort(); }
Постоянное использование методов ContextUtil приводит к коду, который будет сложно перенести из компоненты COM+ в класс, выполняемый вне промежуточной среды COM+. Для решения этой проблемы следует либо использовать методы класса, оборачивающего методы ContextUtil, либо, в крайнем случае, атрибут AutoComplete, который игнорируется для обычных классов. Регистрация обслуживаемых компонент Сборка, содержащая описание одного или нескольких классов компонент, должна быть зарегистрирована как приложение COM+ в каталоге COM+. Для этого сборка должна быть подписанной (strong-named assembly). Неподписанные сборки идентифицируются своим именем, версией и информацией о типе культуры. Подписанная сборка содержит также открытый ключ и цифровую подпись, созданную закрытым ключом. Таким образом, при наличии открытого ключа можно проверить неизменность кода сборки. Для генерации пары из закрытого и открытого ключа служит утилита sn.exe из .NET Framework
SDK.
Для
создания
сборки
из
приведенного
выше
файла
SampleComponent.cs следует выполнить (или внести в make-файл) следующие комманды. sn -k SampleComponent.snk csc /target:library /r:System.EnterpriseServices.dll SampleComponent.cs /keyfile:SampleComponent.snk
После успешного создания подписанной сборки остается зарегистрировать ее в каталоге
COM+.
В
.NET
Framework
существуют
три
способа
регистрации
обслуживаемых компонент: –
с использованием класса System.EnterpriseServices.RegistrationHelper;
–
c использованием утилиты regsvcs.exe;
–
автоматическая
регистрация
сборки
в
момент
создания
экземпляра
обслуживаемой компоненты. 104
Тема 6. Сервисы распределенных систем COM+ Обычно рекомендуется применять первые два способа. Для регистрации созданной сборки выполняется следующая команда. regsvcs SampleComponent.dll
Для удаления сборки из каталога COM+ выполняется аналогичная команда. regsvcs /u SampleComponent.dll
В качестве примера использования созданной компоненты рассмотрим следующий файл. // Файл SampleClient.cs using System; using ServicedComponentSample; class Test { static public void Main() { using(SampleComponent com = new SampleComponent()) { com.Do(); } } } // SampleClient.cs
Использование оператора using приводит к вызову метода Dispose для объекта. Это необходимо для своевременной очистки используемых при создании обслуживаемой компоненты ресурсов COM+. Для компиляции данного файла следует следующую команду. csc /target:exe /r:SampleComponent.dll SampleClient.cs
Для связывания этого приложения с компонентой на удаленном компьютере следует зарегистрировать на нем сборку SampleComponent.dll. Затем, пользуясь оснасткой comexp.msc на удаленном компьютере следует посредника приложения COM+, и установить
посредника
на
компьютере
клиента
с
исполняемым
файлом
SampleClient.exe. Следует отметить, что сборка может быть либо установлена в каталог COM+ в качестве локального приложения, либо как посредник приложения на удаленном компьютере, но не то и другое одновременно. 105
Тема 6. Сервисы распределенных систем COM+ Использование исключений в обслуживаемых компонентах Использование исключений в распределенных системах имеет свои особенности. Если исключение выбрасывается в обслуживаемой компоненте, то оно в общем случае может выйти за границы домена приложения компоненты, то есть подвергнуться сериализации и десериализации в рамках среды .NET Remoting с использованием класса BinaryFormatter. Поэтому все исключения, используемые обслуживаемыми компонентами,
должны
иметь
возможность
System.Runtime.Serialization.Formatter.
Все
сериализации
исключения
из
наследниками
библиотеки
FCL,
наследованные от System.ApplicationException и System.SystemException, имеют такую возможность.
Ниже
приведен
пример
создания
собственного
сериализуемого
исключения. Класс исключения имеет конструктор с параметрами, описанными в теме об
использовании
разработчику
форматеров
достаточно
SoapFormatter
вызвать
и
аналогичный
BinaryFormatter. конструктор
К
счастью,
базового
класса
System.SystemException, который реализует все необходимые действия. // Файл ComException.cs using System; using System.EnterpriseServices; using System.Runtime.Serialization; [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(false)] [Serializable] public class CustomException: System.ApplicationException { public CustomException(SerializationInfo info, StreamingContext context): base(info, context) { } public CustomException(string message): base(message) { } } public class ComSample: ServicedComponent { public ComSample() { } public void Process() { throw new CustomException("случилась неприятность"); } }
106
Тема 6. Сервисы распределенных систем COM+ class MainApp { static public void Main() { ComSample com = new ComSample(); try { com.Process(); } catch(CustomException e) { Console.WriteLine("Исключение: {0}", e.Message); } finally { com.Dispose(); } } } // Файл ComException.cs
Для создания приложения и его регистрации можно выполнить следующие команды. sn -k ComException.snk csc ComException.cs /keyfile:ComException.snk /r:System.EnterpriseServices.dll regsvcs ComException.exe
Создание компенсирующего менеджера ресурсов Компенсирующий менеджер ресурсов позволяет использовать в транзакциях среды COM+ какие-либо ресурсы, которые не имеют прямой поддержки транзакций COM+. Этот
механизм
включает
следующие
классы
из
пространства
имен
System.EnterpriseServices.CompensatingResourceManager: –
журнал операций (log), заполняемый операциями над ресурсом;
–
секретарь (класс Clerk), ведущий журнал операций;
–
компенсатор (наследника класса Compensator), который восстанавливает первоначальное состояние ресурса в случае отката транзакции и вносит изменения в ресурс при успехе транзакции.
Сам менеджер ресурсов должен решить задачу изоляции ресурса в рамках транзакции и ведения журнала операций. При успехе транзакции компенсатор вносит постоянные изменения или отменяет их в соответствии с журналом операций. Следует отметить, что какие-либо временные изменения, производимые менеджером с ресурсом, могут вероятно нарушить требования изоляции транзакции, поскольку могут быть видимыми для внешних по отношению к транзакции объектов. С другой стороны, объекты внутри 107
Тема 6. Сервисы распределенных систем COM+ транзакции должны наблюдать происходящие с ресурсом изменения до успешного завершения транзакции. В качестве примера менеджера ресурсов рассмотрим работу с файлами небольшого размера. Менеджер ресурсов предоставляет следующие сервисы: –
открытие документа на чтение;
–
открытие документа на перезапись;
–
закрыть файл с сохранением изменений.
С целью изоляции транзакций запись в файл происходит в момент успешного завершения транзакции. До этого момента данные хранятся в памяти. Такое решение безусловно не является эффективным с точки зрения траты ресурсов решение, оно используется в качестве примера. При чтении в рамках транзакции документа, уже ранее измененного в этой же транзакции, вместо чтения из файла происходит чтение последней относящийся к этому файлу записи из журнала операций. Журнал операций хранит пары из имени файла (в нижнем регистре) и содержимого файла. Пример компилируется и запускается make-файлом, который можно передать как параметр входящей в состав .NET SDK утилите nmake.exe. Этот файл имеет следующее содержание. # Файл: makefile all: CrmSample.exe # сборка должна быть подписана CrmSample.key: sn -k CrmSample.key CrmSample.exe: CrmSample.cs CrmSample.Key csc /r:System.EnterpriseServices.dll CrmSample.cs /keyfile:CrmSample.key install: # установить приложение COM+ regsvcs CrmSample.exe uninstall: regsvcs –u CrmSample.exe
Поскольку для регистрации сборки как приложения COM+необходимо, чтобы она была подписана, то вызовом утилиты sn.exe из состава .NET SKD создается пара ключей. Затем компилятор языка C# csc.exe создает сборку, используя этот файл ключей. При команде nmake install сборка устанавливается в качестве приложения COM+ вызовом утилиты regsvcs.exe Microsoft Windows. Далее будет рассмотрено содержание файла CrmSample.cs. 108
Тема 6. Сервисы распределенных систем COM+ // Файл CrmSample.cs using System; using System.IO; using System.Collections.Generic; using System.EnterpriseServices; using System.EnterpriseServices.CompensatingResourceManager; [assembly: [assembly: [assembly: [assembly: [assembly:
ApplicationActivation(ActivationOption.Server)] ApplicationAccessControl(false)] ApplicationCrmEnabled] ApplicationName("Seva CRM")] Description("Пример на использование CRM")]
Данные атрибуты сборки из пространства имен System.EnterpriseServices управляют параметрами создаваемого приложения COM+: –
ApplicationActivation – задает тип приложения
COM+ (серверный или
библиотечный); –
ApplicationCrmEnabled – необходим для использования CRM в приложении COM+;
–
ApplicationAccessControl – управляет контролем доступа к приложению, в данном примере – отключен;
–
Атрибут System.ComponentModel.DescriptionAttribute задает описание сборки.
Класс StreamLog содержит статический метод для записи в файл буфера, вызываемый при завершении транзакции. public static class StreamLog { public static void Save(LogRecord log) { if (log.Record is object[]) { object[] record = (object[])log.Record; string fileName = (string) record[0]; byte[] buffer = (byte[]) record[1]; using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { file.Write(buffer, 0, buffer.Length); } } } // Save() } // StreamLog
Класс StreamCache помогает организовать кеширование содержимого файлов, использующихся в транзакции.
109
Тема 6. Сервисы распределенных систем COM+ public class StreamCache { private string fileName; private MemoryStream stream; public MemoryStream Stream { get { return stream;} } public string FileName { get { return fileName;} } public StreamCache(string streamFileName) { fileName = streamFileName; stream = new MemoryStream(); }
Метод Reopen при необходимости открывает повторно закрытый поток. Поскольку такая операция не поддерживается классом MemoryStream, то сначала в массив записывается все содержимое потока, затем создается новый поток, куда записываются сохраненные данные. public void Reopen() { if (!stream.CanRead) { stream.Close(); byte[] buffer = stream.ToArray(); stream.Dispose(); stream = new MemoryStream(); stream.Write(buffer, 0, buffer.Length); stream.Seek(0, SeekOrigin.Begin); } } // Reopen()
При открытии файла для чтения вызывается метод Read, считывающий все содержимое файла в поток типа MemoryStream. public void Read() { byte[] buffer = new byte[32*1024]; using (Stream inputStream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
110
Тема 6. Сервисы распределенных систем COM+ while (true) { int read = inputStream.Read(buffer, 0, buffer.Length); if (read