При каждом создании клиентом ссылки на удаленный объект (точнее, на посредника) на сервере создается новый объект, который существует, пока клиент не удалит ссылку на посредника. При таком методе использования вызовы различных клиентов изолированы друг от друга, и каждый объект сохраняет свое состояние между вызовами, что приводит к наименее рациональному использованию ресурсов памяти сервера (рис. 2.7 ).
Для обеспечения безопасности распределенной системы промежуточная среда должна обеспечивать поддержку трех общеизвестных функций, необходимых для создания безопасных систем.
Проверка подлинности пользователя сервисов компоненты распределенной системы ( аутентификация1)). Проверка подлинности может быть односторонней, когда только сервер убеждается в подлинности клиента, или двухсторонней, когда клиент так же убеждается в подлинности сервера. Ограничение доступа к сервисам компоненты в зависимости от результатов аутентификации (авторизация). Для решения данной задачи промежуточная среда должна поддерживать ограничение доступа, основанное на так называемых ролях (role based security). Поскольку разработчики компоненты не могут обозначить уровни доступа через конкретных пользователей или групп пользователей системы, то они должны использовать некоторые абстрактные роли, которые при развертывании компоненты будут связаны администратором системы с учетными записями пользователей системы. Защита данных, передаваемых между компонентами системы, от просмотра и изменения третьими сторонами. Для этого передаваемые между компонентами сообщения должны подписываться электронной подписью и шифроваться как клиентом, так и сервером.Функции обеспечения безопасности могут обеспечиваться транспортным протоколом, используемым промежуточной средой, самой средой, или ими обеими в совокупности.
Идея удаленного вызова процедур (remote procedure call, RPC) появилась в середине 80-х годов и заключалась в том, что при помощи промежуточного программного обеспечения функцию на удаленном компьютере можно вызывать так же, как и функцию на локальном компьютере. Чтобы удаленный вызов происходил прозрачно с точки зрения вызывающего приложения, промежуточная среда должна предоставить процедуру-заглушку (stub), которая будет вызываться клиентским приложением. После вызова процедуры-заглушки промежуточная среда преобразует переданные ей аргументы в вид, пригодный для передачи по транспортному протоколу, и передает их на удаленный компьютер с вызываемой функцией. На удаленном компьютере параметры извлекаются промежуточной средой из сообщения транспортного уровня и передаются вызываемой функции (рис. 2.2). Аналогичным образом на клиентскую машину передается результат выполнения вызванной функции.
Существует три возможных варианта удаленного вызова процедур.
Синхронный вызов: клиент ожидает завершения процедуры сервером и при необходимости получает от него результат выполнения удаленной функции;Однонаправленный асинхронный вызов: клиент продолжает свое выполнение, получение ответа от сервера либо отсутствует, либо его реализация возложена на разработчика (например, через функцию клиента, удалено вызываемую сервером). Асинхронный вызов: клиент продолжает свое выполнение, при завершении сервером выполнения процедуры он получает уведомление и результат ее выполнения, например через callback-функцию, вызываемую промежуточной средой при получении результата от сервера.Процесс преобразования параметров для передачи их между процессами (или доменами приложения в случае использования .NET) при удаленном вызове называется маршализацией (marshalling). Преобразование экземпляра какого-либо типа данных в пригодный для передачи за границы вызывающего процесса набор байт называется сериализацией. Десериализация – процедура, обратная сериализации – заключается в создании копии сериализованного объекта на основе полученного набора байт. Такой подход к передаче объекта между процессами путем создания его копий называется маршализацией по значению (marshal by value), в отличие от рассматриваемой в следующем разделе маршализации по ссылке.
Процесс сериализации должен быть определен для всех типов данных, передаваемых при удаленном вызове. К ним относятся параметры вызываемой функции и возвращаемый функцией результат. В случае передачи параметров по ссылке сериализации подлежат ссылаемые объекты, поскольку для самих указателей сериализация не может быть применена. Это затрудняет использование механизма удаленного вызова в языках, поддерживающих указатели на объекты неизвестного типа.
Рассмотрим следующий фрагмент кода, заполняющий свойства удаленного объекта информацией о человеке и вызывающий некий удаленный метод для ее обработки.
processing.FirstName = "Иван "; processing.SecondName = "Иванов"; processing.ThirdName = "Иванович"; processing.City = "Москва"; result = processing.Run();Если processing – локальный объект, то в методе Run() он будет иметь доступ к установленным здесь значениям своих методов. Предположим, что processing – удаленный объект. В этом случае возможны, например, следующие варианты.
Используется модель единственного вызова, причем установка свойства объекта рассматривается как вызов метода установки свойства (set в C#). В этом случае к моменту вызова метода Run состояние полей объекта не задано (при отсутствии пула объектов) или неопределенно (при использовании пула объектов).Используется модель единственного вызова, но установка свойств объекта не рассматривается как удаленный вызов. Например, их значения сохраняются на стороне клиента и передаются на сервер в момент вызова метода, где на основе этих значений заполняются поля объекта. В этом случае результат будет корректен.Используется модель активации по запросу клиента, или такой вариант модели единственного вызова, в котором присваивание свойств объекта приводит к передаче данных на сервер без последующей деактивации объекта (по существу такая модель уже не является моделью одного вызова). В этом случае результат так же будет корректен, но присваивание свойств объекту приводит к непроизводительным удаленным вызовам.Используется модель одного экземпляра. В этом случае результат будет некорректным, поскольку поля объекта будут заполняться параллельно разными клиентами.Таким образом, один и тот же код может давать разные результаты в зависимости от модели использования удаленной компоненты, причем только в одном из примеров был получен корректный результат при минимальных накладных расходах. Можно сделать заключение, что использование в программе свойств удаленного объекта приводит к зависимости ее результата от используемой промежуточной среды и ее конфигурации.
Наилучшим вариантом решения этой проблемы представляется отсутствие публичных свойств у удаленного объекта. В таком случае использование удаленного объекта может не отличаться от вызова метода локального объекта. Однако применение такого подхода "в лоб" (так называемый chunky design) может привести к плохо читаемому коду, например следующему.
result = processing.Run("Иванов", "Иван", "Иванович", "Москва");
В отличие от предыдущего фрагмента, это код не является самодокументирующимся, и увеличивает вероятность ошибки в последовательности аргументов вызываемого метода. В данном примере так же наблюдается смешивание двух сущностей: объекта, содержащего данные о человеке, и объекта, производящего с ним операции. Правильный подход заключается в создании маршализируемого по значению класса, содержащего все параметры удаленного вызова, и передача его экземпляра как параметра удаленного вызова метода.
person.FirstName = "Иван"; person.SecondName = "Иванов"; person.ThirdName = "Иванович"; person.City = "Москва"; result = processing.Run(person);
Для многих задач представляет интерес вопрос модификации поведения удаленного объекта путем добавления некоторого дополнительного кода. Рассмотрим следующий фрагмент кода, вызывающий некий математический метод для заданной функции и заданного отрезка значений аргумента. Было бы удобно отделить реализацию самого математического метода от применяемой функции.
В связи с переходом разработчиков прикладных программ от структурной парадигмы к объектной появилась необходимость в использовании удаленных объектов (remote method invocation, RMI). Удаленный объект представляет собой некоторые данные, совокупность которых определяет его состояние. Это состояние можно изменять путем вызова его методов. Обычно возможен прямой доступ к данным удаленного объекта, при этом происходит неявный удаленный вызов, необходимый для передачи значения поля данных объекта между процессами. Методы и поля объекта, которые могут использоваться через удаленные вызовы, доступны через некоторый внешний интерфейс класса объекта. Внешний интерфейс компоненты распределенной системы в таких системах обычно совпадает с внешним интерфейсом одного из входящих в компоненту классов.
В момент, когда клиент начинает использовать удаленный объект, на стороне клиента создается клиентская заглушка, называемая посредником (proxy). Посредник реализует тот же интерфейс, что и удаленный объект. Вызывающий процесс использует методы посредника, который маршализирует их параметры для передачи по сети, и передает их по сети серверу. Промежуточная среда на стороне сервера десериализует параметры и передает их заглушке на стороне сервера, которую называют каркасом (skeleton) или, как и в удаленном вызове процедур, заглушкой (рис. 2.3). Каркас связывается с некоторым экземпляром удаленного объекта. Это может быть как вновь созданный, так и существующий экземпляр объекта, в зависимости от применяемой модели использования удаленных объектов, которые будут рассмотрены ниже.
Весь описанный процесс называется маршализацией удаленного объекта по ссылке (marshal by reference). В отличие от маршализации по значению, экземпляр объекта находится в процессе сервера и не покидает его, а для доступа к объекту клиенты используют посредников. При маршализации же по значению само значение объекта сериализуется в набор байт для его передачи между процессами, после чего следует создание его копии в другом процессе.
При использовании модели единственного экземпляра удаленный объект существует не более чем в одном экземпляре. Созданный объект существует, пока есть хоть один использующий его клиент (рис. 2.6).
При использовании модели единственного объекта вызовы различных клиентов работают с одним и тем же экземпляром удаленного объекта. Поскольку вызовы клиентов не изолированы друг от друга, то используемый объект не должен иметь какого-либо внутреннего состояния. Модель единственного объекта позволяет получить наиболее высокую производительность, поскольку объекты не создаются и не активируются сервером при каждом вызове метода объекта.
При использовании данной модели объект активируется на время единственного удаленного вызова. В наиболее простом случае для каждого вызова удаленного метода объекта клиентом на сервере создается и активируется новый экземпляр объекта, который деактивируется и затем удаляется сразу после завершения удаленного вызова метода объекта. Таким образом, удаленные вызовы разных клиентов изолированы друг от друга. Благодаря удалению объектов после вызова достигается экономное расходование ресурсов памяти, но могут тратиться значительные ресурсы процессора на постоянное создание и удаление объектов. Посредник на клиенте и заглушка на сервер существуют до уничтожения посредника объекта (рис. 2.5).
Данный метод использования удаленных объектов можно рассматривать как некоторый вариант удаленного вызова процедур, поскольку объект не сохраняет свое состояние между вызовами. Тем не менее, сервер использует свои ресурсы на поддержание каркаса и канала между посредником и заглушкой.
Определенным недостатком метода одного вызова является частое создание и удаление экземпляров объектов, поэтому в промежуточным средах может существовать сервис, позволяющий поддерживать некоторое количество уже созданных, но еще не активированных объектов, которые используются для обработки удаленных вызовов. Такой набор объектов, ожидающих своей активации, называется пулом объектов (object pooling). По завершении удаленного вызова объекты деактивируются и могут либо быть помещены в пул и использованы повторно в дальнейшем, либо удаляются, если размер пула достиг некоторого максимального значения. Такая технология позволяет достичь баланса между скоростью обработки запроса и объемом используемых ресурсов сервера. Как видно из описания, в системах с пулом объектов активация не всегда следует непосредственно после создания объекта, а удаление не всегда следует сразу за деактивацией.
Отличительной особенностью метода одного вызова являются наименьшие затраты на организацию системы балансировки нагрузки и ее наибольшая эффективность, поскольку каждый обслуживающий запросы сервер может обработать вызов любого удаленного метода.
Ключевым сервисом промежуточной среды для создания распределенных систем является обеспечение обмена данными между компонентами распределенной системы. В настоящий момент существуют две концепции взаимодействия программных компонент: обмен сообщениями между компонентами и вызов процедур или методов объекта удаленной компоненты по аналогии с локальным вызовом процедуры. Поскольку в настоящее время любое взаимодействие между удаленными компонентами в конечном итоге основано на сокетах TCP/IP, первичным с точки зрения промежуточной среды является низкоуровневый обмен сообщениями на основе сетевых сокетов, сервис которых никак не определяет формат передаваемого сообщения. На базе протоколов TCP или HTTP затем могут быть построены прикладные протоколы обмена сообщений более высокого уровня абстракции для реализации более сложного обмена сообщениями или удаленного вызова процедур.
Удаленный вызов является моделью, происходящей от языков программирования высокого уровня, а не от реализации интерфейса транспортного уровня сетевых протоколов. Поэтому протоколы удаленного вызова должны обязательно базироваться на какой либо системе передачи сообщений, включая как непосредственное использование сокетов TCP/IP, так и основанные на нем другие промежуточные среды для обмена сообщениями. Реализация высокоуровневых служб обмена сообщениями, в свою очередь, может использовать удаленный вызов процедур, основанный на более низкоуровневой передаче сообщений, использующей, например, непосредственно сетевые сокеты. Таким образом, одна промежуточная среда может использовать для своего функционирования сервисы другой промежуточной среды, аналогично тому, как один протокол транспортного или сетевого уровня может работать поверх другого протокола при туннелировании протоколов.
Существует два метода передачи сообщений от одной удаленной системы к другой – непосредственный обмен сообщениями и использование очередей сообщений. В первом случае передача происходит напрямую, и она возможна только в том случае, если принимающая сторона готова принять сообщение в этот же момент времени. Во втором случае используется посредник – менеджер очередей сообщений. Компонента посылает сообщение в одну из очередей менеджера, после чего она может продолжить свою работу. В дальнейшем получающая сторона извлечет сообщение из очереди менеджера и приступит к его обработке.
Простейшей реализацией непосредственного обмена сообщениями является использование транспортного уровня сети через интерфейс сокетов, минуя какое-либо промежуточное программное обеспечение. Однако такой способ взаимодействия обычно не применяется в системах автоматизации предприятия, поскольку в этом случае реализация всех функций промежуточной среды ложится на разработчиков приложения. При таком подходе сложно получить расширяемую и надежную распределенную систему, поэтому для разработки прикладных распределенных систем обычно используются системы очередей сообщений.
Существует несколько разработок в области промежуточного программного обеспечения, реализующие высокоуровневые сервисы для обмена сообщениями между программными компонентами. К ним относятся, в частности, Microsoft Message Queuing, IBM MQSeries и Sun Java System Message Queue. Такие системы дают возможность приложениям использовать следующие базовые примитивы по использованию очередей:
добавить сообщение в очередь;взять первое сообщение из очереди, процесс блокируется до появления в очереди хотя бы одного сообщения; проверить очередь на наличие сообщений; установить обработчик, вызываемый при появлении сообщений в очереди.
Менеджер очереди сообщений в таких системах может находиться на компьютере, отличном от компьютеров с участвующими в обмене компонентами. В этом случае сообщение первоначально помещается в исходящую очередь на компьютере с посылающей сообщения компонентой, а затем пересылается менеджеру требуемой.
Для создания крупных систем обмена сообщениями может использоваться маршрутизация сообщений, при которой сообщения не передаются напрямую менеджеру, поддерживающему очередь, а проходят через ряд промежуточных менеджеров очередей сообщений (рис. 2.1).
Понятие промежуточной среды, обеспечивающей сервисы высокого уровня для инкапсуляции удаленного взаимодействия, появилось в середине 90-х годов, когда выяснилось, что для создания распределенных систем необходима некоторая независящая от приложения и операционной среды "прослойка". Среда CLR так же может рассматриваться как некоторая "промежуточная" среда для выполнения программ на управляемом коде. Поэтому закономерно использовать .NET Framework в качестве основы для создания распределенных приложений.
В настоящий момент в .NET Framework Class Library присутствует поддержка четырех промежуточных сред для построения распределенных систем. Далее они перечислены в порядке даты выпуска промежуточной среды.
Среда Microsoft Message Queuing (MSMQ) поддерживает обмен сообщениями между программными компонентами на основе очередей.Среда Microsoft Enterprise Services основана на разработанной ранее фирмой Microsoft среде COM+, которая позволяет использовать удаленные объекты и распределенные транзакции в локальной сети.Среда ASP .NET Web Services позволяет организовать удаленный вызов на основе общепринятых стандартов, базирующихся на языке XML.Среда .NET Remoting была разработана как универсальная промежуточная среда для использования удаленных объектов.В версии .NET Framework 3.0 предполагается ввести технологию WCF (Windows Communication Foundation), объединяющую все упомянутые технологии построения распределенных систем. Кроме указанных технологий, приложения на .NET Framework могут использовать, например, удаленные вызовы на основе стандарта XML-RPC при подключении дополнительных библиотек.
При разработке программного обеспечения достаточно часто возникает потребность получать извещения о каких-либо событиях, возникающих асинхронно, то есть в некоторые произвольные моменты времени. В распределенных системах так же может возникнуть необходимость использования таких извещений, получаемых от удаленной системы. Можно выделить два подхода к обработке событий – тесно связанные и слабо связанные события. При тесно связанном событии происходит прямое уведомление одной стороны другой стороной. Хотя этот метод можно использовать, например, вместе с однонаправленным асинхронным вызовом, ему свойственен ряд недостатков, ограничивающих его применение в распределенных системах:
обе компоненты системы должны выполняться одновременно;для уведомления нескольких компонент об одном событии уведомляющей стороной должны использоваться механизмы для ведения списка получателей событий;затруднена фильтрация или протоколирование событий.Поэтому в распределенных системах так же применяются слабо связанные события, когда источники события (издатели) не взаимодействуют напрямую с получателями событий (подписчиками). Промежуточная среда в этом случае должна предоставить сервис, позволяющий подписчику подписаться на какое-либо событие или отказаться от подписки, а издателю – инициировать событие для рассылки подписчикам (рис. 2.8).
При использовании слабосвязанных событий подписчики, издатели и менеджер событий могут располагаться на различных компьютерах. Само событие может быть реализовано как, например, вызов менеджером событий некоторого зарегистрированного метода удаленного объекта.
Транзакция – последовательность операций с какими-либо данными, которая либо успешно выполняется полностью, либо не выполняется вообще. В случае невозможности успешно выполнить все действия происходит возврат к первоначальным значениям всех измененных в течение транзакции данных (откат транзакции). Транзакция должна обладать следующими качествами.
Атомарность. Транзакция выполняется по принципу "все или ничего". Согласованность. После успешного завершения или отката транзакции все данные находятся в согласованном состоянии, их логическая целостность не нарушена. Изоляция. Для объектов вне транзакции не видны промежуточные состояния, которые могут принимать изменяемые в транзакции данные. С точки зрения "внешних" объектов, до успешного завершения транзакции они должны иметь то же состояние, в котором находились до ее начала.Постоянство. В случае успешности транзакции сделанные изменения должны иметь постоянный характер (т.е. сохранены в энергонезависимой памяти).Транзакции являются основой приложений, работающих с базами данных, однако в распределенной системе может быть недостаточно использования только транзакций систем управления базами данных. Например, в распределенной системе в транзакции может участвовать несколько распределенных компонент, работающих с несколькими независимыми базами данных (рис. 2.9).
Распределенной называется транзакция, охватывающая операции нескольких взаимодействующих компонент распределенной системы. Каждая из этих компонент может работать с какими-либо СУБД или иными службами, например, использовать очереди сообщений, или даже работать с файлами. При откате транзакции все эти операции должны быть отменены. Для этого необходимо выполнение двух условий:
промежуточная среда должна поддерживать управление распределенными между несколькими компонентами транзакциями;компоненты распределенной системы не должны работать с какими-либо службами или ресурсами, которые не могут участвовать в транзакции.Распределенные транзакции являются важнейшим элементом поддержания целостности данных в распределенной системе.
Поэтому для более широкого их применения промежуточная среда может содержать механизмы, которые при необходимости (и определенных затратах времени на написание кода) позволят использовать в распределенных транзакциях внешние службы, не поддерживающие транзакции. Такой механизм называется компенсирующим менеджером ресурса (compensating resource manager). Компенсация в данном случае означает возврат ресурса к первоначальному состоянию при откате транзакции.
В настоящее время происходит формирование и стандартизация еще одного понятия, связанного с поддержкой целостности данных – хозяйственной деятельности (business activity) применительно к распределенным системам. Деятельность обычно является отражением некоторого реального процесса, например, покупки в магазине: от оформления заказа до подтверждения доставки курьером. Деятельность может включать в себя транзакции (оформление заказа покупателя, заказ товара у поставщика, и так далее – до подтверждения доставки покупателем). В отличие от транзакции, время жизни которой предполагается коротким, деятельность может длиться в течение очень долгого времени (например, месяца). Деятельность может поддерживать отмену сделанных изменений (например, оформление возврата товара поставщика при отказе покупателя) путем использования компенсирующих задач.
Программные компоненты с точки зрения пользователей своих сервисов можно разделить на две категории:
компоненты без сохраняемого между удаленными вызовами своих методов внутреннего состояния (stateless components);компоненты с внутренним состоянием, сохраняемым между удаленными вызовами своих методов (statefull components).Под состоянием в данном случае понимается совокупность значений полей реализующих компоненту объектов, хранящихся в памяти сервера. Если компонента в ходе своей работы сохраняет какие-либо данные во внешнем хранилище, например в базе данных или очереди сообщений, это обычно не рассматривается как ее внутреннее состояние.
Модель единственного вызова не сохраняет состояния удаленного объекта между вызовами его методов, в силу чего данная модель может использоваться только с распределенными компонентами без внутреннего состояния. Модель одного экземпляра может быть использована для вызова компонент с внутренним состоянием, но это вряд ли часто имеет смысл, поскольку ее состояние будет меняться каждым из клиентов в произвольном порядке. Модель активации по запросу клиента может быть использована с любыми компонентами, но для компонент без внутреннего состояния такой подход обычно ведет к непроизводительному расходу памяти при некотором выигрыше в затратах времени процессора по сравнению с моделью одного вызова.
Компоненты без сохранения внутреннего состояния, используемые вместе с моделью единственного вызова с пулом объектов, имеют наибольшие возможности масштабирования системы при оптимальном балансе между затратами памяти и нагрузкой на процессор.