|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Распределенное объектно-ориентированное программирование
Распределенные объекты — это объекты, которые являются частью одного приложения, но размещены в различных адресных пространствах Адресные пространства могут относиться к одному или различным компьютерам, связанным сетью или другими средствами коммуникации. Объекты, включенные в приложение, могли быть изначально нацелены на совместную работу или разрабатываться различными отделами, подразделениями, компаниями или организациями в различное время и с разными целями. В категорию распределенных объектно-ориентированных приложений мотут попадать приложения из большого диапазона: от одноразовой собранной совместными усилиями коллекции несвязанных объектов до мульти-приложения, «разновозрастные» объекты которого разбросаны по сети Internet. Местоположение объектов может быть самым разнообразным: intranet (корпоративная локальнал сеть повышенной надежности с ограниченным доступом, использующал сетевые стандарты и сетевые программно-аппаратные средства, аналогичные Internet), extranet (экстрасеть— объединение корпоративных сетей различных компаний, взаимодействующих друг с другом через Internet) и Internet. Согласно большинству описаний распределенных объектов, они (объекты) могут быть реализованы в различных языках, например С++, Java, Eiffel и Smalltalk. Распределенные объекты могут играть множество ролей. Б одних сигуациях такой объект (или коллекция объектов) используется в качестве сервера, который способен обеспечить услуги, например, по доступу к базе данных, по обработке данных или их передаче. В других ситуациях объекты играют роль клиентов. Распределенные объекты можно использовать в таких объединенных моделях решения проблем, как «классная доска» и мультиагентные системы. Помимо объединенных моделей, распределенные объекты могут быть полезны для реализации таких парадигм параллельного программирования, как SPMD и MPMD. Объектам одного приложения не требуется специального протокола для связи между собой. Коммуникация достигается за счет обычного вызова методов, передачи параметров и использования глобальных переменных. Но, поскольку распределенные объекты расположены в различных адресных пространствах, здесь не обойтись без методов межпроцессного взаимодействия и во многих случалх необходимо сетевое программирование. Необходи м ость создания распределенных приложений обусловлена разны м и причина м и. • Необходимые ресурсы (например, базы данных, специализированные процессоры, модемы, принтеры и т.п.) расположены на рааличных компьютерах. Клиентские объекты (объекты, формирующие запрос на обслуживание) взаимодействуют с серверными объектами (объектами, реагирующими на запрос обслуживания) для получения доступа к этим ресурсам. • Для выполнения некоторой важной работы или решения насущной проблемы необходимо скооперировать объекты, различающиеся временем разработки, разработчиками и местоположением. • Агенты, реализованные как объекты, отличающиеся узкой специализацией, требуют собственных адресных пространств, поскольку они запускаются как отдельные процессы. • Объекты используются в качестве базовых модулей, которые реализованы как отдельные программы, каждая из которых имеет собственное адресное пространство. • Объекты реализованы в SPMD- или МРМП-архитектуре, рассчитанной на использование параллельного программирования, причем эти объекты расположены в различных процессах и на различных компьютерах. В объектно-ориентированном приложении выполняемая программой работа делится между несколькими объектами. Эти объекты представляют собой модели определенной реальной личности, реального места, предмета или идеи. Выполнение объектно-ориентированной программы вынуждает ее объекты взаимодействовать между собой в соответствии с правилами, заложенными в этой модели. В распределенном объектно-ориентированном приложении некоторые взаимодействующие объекты будут создаваться различными программами, которые, возможно, выполняются на различных компьютерах. В главе 3 упоминалось о том, что каждая выполняемал программа включает один или несколько процессов. Каждый процесс обладает собственными ресурсами. Например, любой процесс имеет собственную память, дескрипторы файлов, стековое пространство, идентификатор и т.п. Задачи, выполняемые в одном процессе, не имеют прямого доступа к ресурсам, принадлежащим другому процессу. Если задачам, выполняемым в одном процессе, необходима информация, хранимая в памяти другого процесса, то эти два процесса должны явно обменяться информацией с помощью файлов, каналов, общей памяти, переменных среды или сокетов. Объекты, которые принадлежат различным процессам и нуждаются во взаимодействии между собой, также должны использовать один из перечисленных выше способов явного обмена информацией. Как правило, С++-разработчик при разработке распределенного объектно-ориентированного приложения сталкивается с необходимостью решения следующих проблем. • Деко м позиция задачи и ее решения на м ножество объектов, приче м некоторые из них будут принадлежать рааличным процесса м и раз м ещаться на разных компьютерах. • Обеспечение связи между объектами, принадлежащими различным процесса м (адресным пространствам). • Синхронизация взаимодействия между локальны м и и удаленны м и объектами. • Обработка ошибок и исключений в распределенной среде. Декомпозиция задачи и инкапсуляция ее решения Проектирование объектно-ориентированного программного обеспечения — это процесс перевода требований к ПО в проект, в котором с помощью объектов моделируется каждый аспект разрабатываемой системы и выполняемой ею работы. Центральное место в этом проекте отводится структуре и иерархии коллекций объектов, а также их взаимоотношениям и взаимодействиям. Для поддержки понятия модели ПО в С++ используется ключевое слово class. Существует два базовых типа моделей. Первый тип модели — масштабированное представление некоторого процесса, концепции или идеи. Этот тип модели используется для анализа или экспериментирования. Например, класс применяется для разработки модели молекулы, т.е. с помощью концепции С++-класса можно смоделировать гипотетическую структуру некоторого химического процесса, происходящего в молекулах. Программным путем можно затем изучить поведение молекулы при внедрении новых групп атомов. Второй тип модели ПО — воспроизведение некоторой реальной задачи, процесса или идеи. Цель этой модели — заставить некоторую часть системы ПО или приложения функционировать подобно ее «прототипу». В этом случае ПО занимает место некоторого компонента или некоторого физического предмета в неавтоматизированной системе. Например, мы можем использовать концепцию класса для моделирования калькулятора. При корректном моделировании всех его характеристик и поведения можно создать экземпляр этого класса и использовать в качестве настоящего калькулятора. Программный калькулятор здесь будет играть роль реального калькулятора. Таким образом, смоделированный нами класс может служить в качестве виртуального лублера некоторого реального лица, места, предмета или идеи. Главное в программной модели — ухватить суть реального предмета. Декомпозиция (decomposition) — это процесс разделения задачи и ее решения на ряд подзадач, коллекций объектов и принципов взаимоотношений между этими объектами. Аналогично инкапсуляция (encapsulation) — это моделирование характеристик, атрибутов и поведения некоторого лица, места, предмета или идеи с помощью С++-конструкции class. Такое моделирование (инкапсуляция) и декомпозиция являются частью этапа проектирования объектно-ориентированного ПО. Объектно-ориентированные приложения, которые содержат распределенные объекты, вносят в процесс проектирования дополнительный уровень сложности. С точки зрения «чистого» проектирования местоположение объектов в приложении не должно влиять на разработку атрибутов и характеристик этих объектов. Класс — это модель и, если местоположение не является частью этой модели, то даже самое «крайнее» расположение объектов (экземпляров этого класса) не должно иметь значение. Однако объекты существуют не в вакууме. Они взаимодействуют и обмениваются информацией с другими объектами. Если объекты (участники взаимодействия) расположены на разных компьютерах, и, возможно, даже в различных сетях, то оказывается, что фактор местоположения объектов необходимо с самого начала включать в процесс проектирования ПО. И хотя насчет того, на каком именно этапе проектирования следует рассматривать этот фактор, специалисты существенно расходятся во мнениях, тем не менее все единолушны в том, что его необходимо рассматривать. Дело в том, что обработка ошибок и исключений при взаимодействии объектов, расположенных в различных процессах или на различных компьютерах, отличается от обработки ошибок и исключений при взаимодействии объектов одного и того же процесса. Кроме того, связи и взаимодействия между объектами одного процесса реализуются совершенно не так, как при расположении объектов в различных процессах, которые могут выполняться на разных компьютерах. Это нужно иметь в виду еще на раннем этапе проектирования. В распределенном объектно-ориентированном приложении вся его работа делится между объектами и реализуется в виде функций-членов различных объектов. Объекты должны быть логически разделены согласно определенной модели декомпозиции работ (Work Breakdown Model — WBM). Они могут быть разделены согласно моделям типа «клиентс - сервер», «изготовитель-потребитель», равноправных узлов, «классной доски» или мультиагентной системы. Логическая структура каждой такой модели (с особенностями распределения объектов) показана на рис. 8.1. Во всех моделях, показанных на рис. 8.1, предполагается, что объекты могут быть на одном и том же или на разных компьютерах (главное — они принадлежат разным процессам). Уже сам факт принадлежности рааличным процессам делает объекты распределенными [13]. Все модели представляют рааличные подходы к распределению работы приложения между объектами. Взаимодействие между распределенными объектами
Если объекты относятся к одному и тому же процессу, то в качестве средств межобъектно г о «общения» можно использовать механизм передачи параметров, вызовы обычных методов и использование г лобальных переменных. Если объекты принадлежат различным процессам, выполняемым на одном компьютере, то средствами ком м уникации между объекта м и м огут служить файлы, каналы, очереди c дисциплиной обслуживания «первы м пришел — первы м обслужен», разделяе м ал па м ять, буферы об м ена или пере м енные среды. Если же объекты «прописаны» на различных компьютерах, то в качестве средств связи придется использовать сокеты, вызовы удаленных процелур и дру г ие типы средств сетево г о про г раммирования. При это м мы должны поду м ать не только о то м, как булут общаться объекты в распределенном приложении, но и о том, посредство м че г о будет реализовано это об щ ение. Объектно- ориентированные приложения могут включать как простые типы данных, так и довольно сложные, а именно классы, определенные пользователем. Такие классы часто используются для связи между объектами. Поэтому связь между распределенными объектами будет обеспечиваться не только с помощью простых встроенных типов данных (например int, float или double), но и посредством классовых типов, определенных пользователем, без которых некоторые объекты не смогут выполнить свою работу. Кроме того, необходимо позаботиться о том, чтобы у одних объектов была возможность вызывать методы других объектов, расположенных в других адресных пространствах. Более того, необходимо прелусмотреть возможность для одного объекта «знать» о методах удаленных объектов. В то время как язык С++ поддерживает средства объектно-ориентированного программирования, в нем не предусмотрено никаких встроенных средств по обеспечению связи между распределенными объектами. Он не содержит никаких встроенных методов для локализации удаленных объектов и формирования к ним запросов. Для реализации связи между распределенными объектами разработан ряд протоколов. Двумя наиболее важными из них являются IIOP (Internet Inter-ORB Protocol — протокол, определяющий передачу сообщений между сетевыми объектами по TCP/IP) и RMI (Remote Method Invocation — вызов удаленных методов). С помощью этих протоколов могут общаться объекты, расположенные практически в любом мести сети. В этой главе мы рассмотрим методы реализации распределенных объектно-ориентированных программ с использованием упомянутых протоколов и спецификации CORBA (Common Object Request BrokerArchitecture). Спецификация CORBA представляет собой промышленный стандарт для определения отношений, взаимодействий и связей между распределенными объектами. IIOP и GIOP — два основных протокола, с которыми работает спецификация CORBA. Эти протоколы хорошо согласуются с протоколом TCP/IP. CORBA — самый простой и наиболее гибкий способ добавления средств распределенного программирования в среду С++. Средства, предоставляемые спецификацией CORBA, реализуют поддержку двух основных моделей объектно-ориентированного параллелизма, которые мы используем в этой книге: «классная доска» и мультиагентные системы. Поскольку спецификация CORBA отражает принципы объектно-ориентированного программирования, с ее помощью можно реализовать приложения довольного широкого диапазона: от миниатюрных до очень больших. В этой книге мы используем MICO [14] — открытую реализацию спецификации CORBA. MICO-реализация поддерживает основные CORBA-компоненты и службы. С++ взаимодействует с MICO посредством коллекции классов и библиотек классов. Спецификация CORBA поддерживает распределенное объектно-ориентированное моделирование на каждом его уровне. Синхронизация взаимодействия локальных и удаленных объектов Для синхронизации доступа к данным и ресурсам со стороны нескольких объектов, принадлежащих различным процессам, но расположенных на одном компьютере, можно использовать мьютексы и семафоры, поскольку каждый процесс, хотя и отделенный от других, все же получает доступ к системной памяти компьютера. Эту системную память функционально можно рассматривать как разновидность памяти, разделяемой между процессами. Но если процессы распределены между различными компьютерами, то следует помнить, что разные компьютеры не имеют никакой общей памяти, и поэтому схемы синхронизации в этом случае должны быть реализованы по-другому. Синхронизация доступа (в зависимости от используемой WBM-модели) может потребовать интенсивного взаимодействия между распределенными объектами. Поэтому мы расширим традиционные методы синхронизации с помощью коммуникационных возможностей спецификации CORBA. Обработка ошибок и исключений в распределенной среде Возможно, одной из самых сложных областей обработки исключительных ситуаций или ошибок в распределенной среде считается область частичных отказов. В распределенной системе могут отказать один или несколько компонентов, в то время как другие компоненты будут функционировать в «предположении», что в системе все в полном порядке. Если такая ситуация (например, отказ одной функции) возникает в локальном приложении, т.е. когда все компоненты принадлежат одному и тому же процессу, об этом нетрудно уведомить все приложение в целом. Но для распределенных приложений все обстоит иначе. На одном компьютере может отказать сетевая карта, а объекты, выполняемые на других компьютерах, могут вообще не «узнать» о том, что где-то в системе произошел отказ. Что случится, если один из объектов попытается связаться с другим объектом и вдруг окажется, что сетевые связи с ним оборвались? Если при использовании модели равноправных узлов (в которой мы формируем различные группы объектов по принципу решения различных аспектов проблемы) одна из групп откажет, то как об этом отказе «узнают» другие группы? Более того, какое поведение мы должны «навязать» системе в такой сигуации? Должен ли отказ одного компонента приводить к отказу всей системы? Если даст сбой один клиент, то должны ли мы прекратить работу сервера? А если откажет сервер, то нужно ли останавливать клиент? А что, если сервер или клиенты продемонстрируют лишь частичные отказы? Поэтому в распределенной системе, помимо «гонок» данных и взаимоблокировок, мы должны также найти способы справляться с частичными отказами компонентов. И снова-таки подчеркиваем, важно найти распределенный подход к С++-механизму обработки исключительных сигуаций. Для начала нас удовлетво-рятвозможности, предоставляемые спецификацией CORBA. Доступ к объектам из других адресных пространств Объекты, разделяющие одну область действия (видимости), могут взаимодействовать, получал доступ друг к другу по именам, псевдонимам или с помощью указателей. Объект доступен только в случае, если «видимо» его имя или указатель на него. Видимость имен объектов определяется областью действия. С++ рааличает четыре основ-ныхуровня областей действия: • блока; • функции; • файла; • класса. Вспомните, что блок в С++ определяется фигурными скобками {}, поэтому присваивание значения Y переменной X в листинге 8.1 недопустимо, так как переменная Y видима только внутри блока. Функции main() неизвестно имя переменной Y за пределами блока, конец которого обозначен закрывающейся фигурной скобкой. // Листинг 8.1. Простой пример области действия блока int main(int argc, char argv[]) { int X; int Z; { int Y; Z = Y; // Вполне правомочное присваивание. //.. . } X = Y ; // Неверно, поскольку имя Y уже не определено. } Однако имя Y видимо для любого другого кода из того же блока, в котором определена переменная Y. Имя, объявляемое внутри функции или ее объявления, получает область видимости этой функции. В листинге 8.1 переменные X и Z видимы только для функции main (), и к ним нельзя получить доступ из других функций. Понятие области видимости файла относится к исходным файлам. Поскольку С++-программа может состоять из нескольких файлов, мы можем создавать объекты, которые видимы в одном файле и невидимы в другом. Имена, обладающие областью видимости файла, видимы, начиная с местоположения их объявления и заканчивая концом исходного файла. Имена с областью видимости файла не должны объявляться ни в одной из функций. Обычно их называют глобальными переменными. Имена, которые характеризуются областью видимости объекта, видимы для любой функции-члена, объявленной как часть этого объекта. Мы используем область видимости объекта в качестве первого уровня доступа к членам объекта. Закрытый, защищенный и открытый интерфейсы объекта определяют второй уровень. И хотя само имя объекта может быть видимым, закрытые и защищенные его члены тем не менее имеют ограниченный доступ. Область действия просто сообщает нам, видимо ли имя объекта. В нераспределенной программе область действия ассоциируется с единым адресным пространством. Два объекта в одном и том же адресном пространстве могут получать доступ друг к другу по имени или указателю и взаимодействовать, просто вызывал методы друг друга. // Листинг 8.2. Использование объектов, которые вызывают // методы других объектов из того же // адресного пространства //.. . some_object А; another_object В; dynamic_object *C; C = new dynamic_object; //... B.doSomething(A.doSomething() ); A.doSomething(B.doSomething() ); C->doMore (A.doSomething () ) ; //... В листинге 8.2 объекты А и В находятся в одной области видимости, т.е. объект В видим для объекта А, а объект А видим для объекта В. Объект А может вызывать функции-члены объекта В, и наоборот. А что можно сказать об областях види м ости, если два объекта находятся на различных компьютерах? Что происходит, когда объект В создается другой программой и «получает прописку» совершенно в другом адресном пространстве? Как объект А узнает о существовании объекта В и как (что особенно важно) объект А узнает имя и интерфейс объекта В? Каким образом объект А сможет вызывать функции-члены, принадлежащие объекту В, если В — часть другой программы? В листинге 8.2 объекты А и В создаются во время компиляции, а объект С — во время выполнения. Все они являются частями одной и той же программы, обладают одной областью видимости, а их адреса принадлежат адресному пространству одного и того же процесса. Чтобы процесс мог выполнить инструкцию, ему нужно знать ее адрес. При компиляции программы, представленной в листинге 8.2, адреса объектов А и В хранятся в выполняемом файле. Сле д овательно, процесс, который выполняет программу из листинга 8.2, будет знать местоположение объектов А и В. Адрес объекту С присваивается во время выполнени я программы, т.е. его точный адрес станет известен только тог д а, когда будет вызвана функция new (). Однако указатель на объект С бу д ет содержать адрес в пределах того же пространства, в котором размещаются объекты А и В, и, следовательно, процесс для получения доступа к объекту С воспользуется этим указателем. Таким образом, доступ к каждому объекту осуществляется на основе доступа к их адресам (прямого или косвенного). Имя переменной объекта — это просто псевдоним для его адреса. Если имя объекта попадает в рамки нашей области видимости, то мы можем получить к нему доступ. Проблема в том, как связать удаленный объект с нашей локальной областью видимости. Для того чтобы получить доступ к объекту D, который находится в другом адресном пространстве, нам необходим некоторый способ ввода адреса удаленного объекта в наш выполняющийся процесс, т.е. нужно научиться связывать удаленный объект с нашей локальной областью видимости. Нам требуется видимое имя, которое бы служило псевдонимом для адреса в другом процессе, причем этот процесс может выполняться даже на другом компьютере. В некоторых случалх этот самый «другой» компьютер может быть подключен к другой сети! Было бы весьма удобно запросить удаленный объект с помощью некоторого согласованного описания и получить ссылку для адреса удаленного объекта. Имея ссылку, мы могли бы взаимодействовать с этим объектом из нашей локальной области действия. Именно для таких нужд распределенного программирования и можно использовать CORBA-реализацию. . IOR-доступ к удаленным объектам Объектнал ссылка специального типа IOR (Interoperable Object Reference) — это стандартный формат объектной ссылки для распределенных объектов. Каждый CORBA-объект имеет IOR-ссылку. IOR-ссылка — это дескриптор, который уникально идентифицирует объект. В то время как обычный указатель содержит простой машинный адрес для объекта, IOR-ссылка может содержать номер порта, имя хоста (имя компьютера в сети), объектный ключ и пр. В С++ для доступа к динамически создаваемым объектам используется указатель. Указатель содержит информацию о том, где в памяти компьютера расположен объект. При разыменовании указателя на объект используется полученный адрес для доступа к членам этого объекта. Однако процесс разыменования указателя на объект (с целью получения доступа к нему) требует больших усилий, когда этот объект находится в другом адресном пространстве и, возможно, на другом компьютере. Указатель в этом случае должен содержать достаточно информации, чтобы сообщить точное местоположение объекта. Если объект расположен в другой сети, указатель должен содержать (прямо или косвенно) сетевой адрес, сетевой протокол, имя хоста, адрес порта, объектный ключ и физический адрес. Стандартнал IOR-ссылка действует как разновидность распределенного указателя на удаленный объект. Набор компонентов, содержащихся в IOR-ссылке под протоколом IIOP, показан на рис. 8.2. Пон я тие переносимой (portable) объектной ссылки — это важный этап на пути к достижению распределенной обработки данных. Оно позволяет использовать локальные ссылки на удаленные объекты практически везде (в Internet или intranet) и имеет важные последствия для мультиагентных систем, в которых агентам приходится перемещаться между системами и по всему пространству Internet. Стандарт IOR создает основу для мобильных объектов и распределенных агентов. После того как ваша программа получит доступ к IOR-ссылке объекта, можно использовать брокер объектных запросов (Object Request Broker — ORB) для взаимодействия с удаленным объектом посредством вызова методов, механизма передачи параметров, возврата значений и т.п. Логические компоненты IOR-ссылки:
Рис. 8.2. Набор компонентов, содержащихся в IOR-ссылке подпротоколом IIOP Брокеры объектных запросов (ORB) ORB-брокер действует от имени программы. Он посылает сообщения удаленному объекту и возвращает сообщения от него. Поведение ORB-брокера можно сравнить с посредником между локальными и удаленными объектами. ORB-брокер решает все вопросы, связанные с маршрутизацией запроса от программы к удаленному объекту и с маршрутизацией ответа программе, принятого от удаленного объекта. Такое посредничество делает коммуникации между системами практически прозрачными. ORB-брокер избавляет программиста от необходимости программирования сокетов между процессами, выполняющимися на различных компьютерах. И точно так же он устраняет необходимость в программировании каналов и очередей с FIFO-дисциплиной между процессами, выполняющимися на одном компьютере. Он берет на себя немалый объем сетевого программирования, без которого не обойтись при создании распределенных программ. Более того, он стирает различия между операционными системами, языками программирования и аппаратными средствами. При программировании локальных объектов программисту больше не нужно беспокоиться о том, на каком языке реализованы удаленные объекты, на какой платформе они выполняются и к какой сети они «приписаны»: Internet или локальной intranet. ORB-брокер использует IOR-ссылки, чтобы упростить взаимодействие между компьютерами, сетями и объектами. Обратите внимание на то, что IOR-ссылка (см. рис. 8.2) содержит информацию, которая может быть использована для TCP/IP-соединений. Мы представили лишь частичное описание IOR-компонентов, поскольку IOR-дескриптор должен быть «черным ящиком» для разработчика. ORB-брокер использует IOR-ссылки, чтобы найти объект назначения. Обнаружив объект, ORB-брокер активизирует его и передает аргументы, необходимые для вызова этого объекта. ORB-брокер ожидает завершения обслуживания запроса и возвращает вызывающему объекгу ожидаемую информацию или исключение, если вызов метода оказался неудачным. Упрощенная последовательность действий, выполняемых ORB-брокером от имени локального объекта, показана на рис. 8.3. Действия, перечисленные на рис. 8.3, представляютупро щ енную схему того, что делает ORB-брокер, взаимодействуя с удаленным объектом. Эти действия практически незаметны для локального объекта. Локальный объект вызывает один из методов удаленного объекта, а ORB-брокер делает «свою работу» от имени локального объекта. ORB-брокер выполняет большой объем обработки, заключенный всего лишь в нескольких строках кода. Обычно распределенное объектно-ориенти-рованное приложение состоит по крайней мере из двух программ. Каждая программа имеет один или несколько объектов, которые взаимодействуют друг с другом, «пересекая» адресные пространства. Характер взаимодействия объектов определяется отношениями «клиент-сервер», «изготовитель-потребитель» или базируется на принципе равноправия (модель равноправных узлов). Следовательно, если у нас есть две программы, то одна будет действовать как клиент, а другая — как сервер, или одна — как изготовитель, а другая — как потребитель, либо обе они будут равноправными. В программе 8.1 реализован потребитель, который вызывает простой удаленный объект калькулятора. На примере этой программы демонстрируется, как можно получить доступ к удаленному объекгу, а также как инициализируется и используется ORB-брокер. УПРОЩЕННАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ ДЕЙСТВИЙ ORB-БРОКЕРА ПРИ ВЫЗОВЕ МЕТОДА УДАЛЕННОГО ОБЪЕКТА _ 1. Найти удаленный объект. _ 2. Активизировать модуль, содержа щ ий искомый объект, если таковой е щ е не активизирован. _ 3. Передать аргументы удаленному объекту. _ 4. Ожидать ответа после вызова метода удаленного объекта. _ 5. Вернугьлокальномуобъекту информацию или исключение, если вызовудаленного метода оказался неуспешным. _ Рис. 8.3. Упрощенная последовательность действий, выполняемых ORB-брокером от имени локального объекта // Программа 8.1 1 using namespace std; 2 #include «adding_machine_impl.h» 3 #include <iostream> 4 #include <fstream> 5 #include <string> 6 7 8 int main(int argc, char *argv[]) 9 { 10 CORBA::ORB_var Orb = CORBA::ORB_init(argc, argv, «mico-local-orb»); 11 CORBA::BOA_var Boa = Orb->BOA_init(argc,argv,«mico-local-boa»); 12 ifstream In(«adding_machine.objid»); 13 string Ref; 14 if('In.eof()){ 15 In » Ref; 16 } 17 In.close(); 18 CORBA::Object_var Obj = Orb->string_to_object(Ref.data()); 19 adding_machine_var Machine =adding_machine::_narrow(Obj); 20 Machine->add(700); 21 Machine->subtract(250); 22 cout << «Результат равен " « Machine->result()« endl; 23 return(0); 24 } 25 26 При выполнении строки 10 ORB-брокер инициализируетс я. Строка 15 обеспечивает считывание из файла IOR-ссылки на объект adding_machine. Одно из прекрасных свойств IOR-ссылки состоит в том, что ее можно хранить как простую строку и передавать другим программам. Передачу IOR-ссылки проще всего реализовать с помощью аргументов командной строки, переменных среды или файлов. IOR-ссылку можно отправить по электронной почте или с помощью протокола передачи файлов (File Transfer Protocol — FTP). IOR-ссылки совместно используют файловые системы, и их можно загружать с Web-страниц. Если некоторая программа имеет IOR-ссылку на удаленный объект, то для доступа к нему можно использовать ORB-брокер. Другие методы связи между объектами с помощью IOR-ссылок будут рассмотрены ниже в этой главе. Но для начала вполне достаточно использования файловых систем. Итак, в программе 8.1 IOR-ссылка была получена путем преобразования объектной ссылки в «строковую» форму (с использованием ORB-брокера удаленного калькулятора) и записана в файл. При выполнении строки 18 локальный объект Orb преобразует «строковую» IOR-ссылку обратно в объектную. В строке 19 эта объектнал ссылка используется для реализации объекта adding_machine. Обратите внимание на то, что при вызове методов этого объекта adding_machine выполняется соответствующий код удаленного калькулятора (см. строки 20, 21 и 22). Machine->add(700) ; Machine->subtract(250) ; cout « «Результат равен " « Machine->result() « endl; И хотя вызовы этих методов сделаны в нашей локальной области види м ости, они относятся к выполняемому колу в другом адресном пространстве (в данном случае — даже к другому компьютеру). Для разработчика местоположение объекта Machine как будто перестает иметь значение. После создания (в строке 19) этот объект используется как любой другой объект С++. И хотя существуют весьма значительные различия между вызовами локальных и удаленных объектов [15], объектно-ориентированное представление, тем не менее, поддерживается, и с точки зрения объектно-ориентированного программирования удаленные объекты ведут себя как локальные. Код, представленный в программе 8.1, является кодом клиентской части приложения (или кодом «потребителя»), поскольку в нем используются возможности объекта adding_machine. Поэтому теперь (для получения завершенного приложения калькулятора) нам нужен код «ответной части», который реализует объект adding_machine. Код этого второго компонента представлен в программе 8.2. // Программа 8.2 1 #include <iostream> 2 #include <fstream> 3 #include «adding_machine_impl.h» 4 5 6 7 8 int main(int argc, char *argv[]) 9 { 10 CORBA::ORB_var Orb = CORBA: :ORB_init(argc,argv,«mico-local-orb»); 11 CORBA::BOA_var Boa = Orb->BOA_init(argc,argv,«mico-local-boa») ; 12 adding_machine_impl *AddingMachine =new adding_machine_impl; 13 CORBA::String_var Ref = Orb->object_to_string(AddingMachine); 14 ofstream Out(«adding_machine.objid»); 15 Out « Ref « endl; 16 Out.close() ; 17 Boa->impl_is_ready (CORBA: : ImplementationDef : :_nil () ) ; 18 Orb->run(); 19 CORBA: :release(AddingMachine) ; 20 return(0); 21 } 22 23 Обратите внимание на то, что программа-«изготовитель» также должна инициализировать объект Orb (в строке 10). Это — одно из важных требований, предъявляемых к CORBA-ориентированным программам, поскольку каждая программа реализует взаимодействие с удаленными объектами с помощью ORB-брoкepa. Именно поэтому инициализация ORB-объекта— первое действие, которое должна выполнить CORBA-программа. В строке 12 объявляется реальный объект adding_machine . Это именно тот объект, с которым в действительности связывается программа 8.1. В строке 13 объектная ссылка на реальный объект adding_machine преобразуется в «строковую» форму, а затем записывается в обычный текстовый файл, чтобы ее можно было без труда прочитать. После того как IOR-ссылка записана в файл, объект Orb ожидает запроса. При каждом вызове одного из его методов этот объект выполняет соответствующее арифметическое действие (сложение или вычитание). Значение результата передается посредством вызова метода result() объекта adding_machine. Программы 8.1 и 8.2 демонстрируют базовую структуру, которую должны иметь CORBA-программы. Код, создающий объект adding_machine, начинается с объявления его CORBA-класса. Каждый CORBA-объект начинается как IDL-проект (Interface Definition Language — язык описания интерфейсов). Язык описания интерфейсов (IDL):более «пристальный» взгляд на CORBA-объекты Язык описания интерфейсов (IDL) — стандартный язык объектно-ориентированного проектирования, который используется для разработки классов, предназначенных для распределенного программирования. Он применяется для отображения интерфейса класса и отношений между классами, а также для определения прототипов функций-членов, типов параметров и типов значений, возвращаемых функциями. Одно из основных назначений языка IDL — отделить интерфейс класса от его реализации. Но дл я определени я самих функций-членов и членов данных IDL не используетс я. Язык IDL определ я ет только интерфейс функции. Основные ключевые слова IDL перечислены в табл. 8.1.
Ключевые слова, перечисленные в табл. 8.1, представл я ют собой зарезервированные слова, используемые в CORBA-программах. Помимо определени я интерфейса функций дл я класса, я зык IDL используетс я дл я определени я отношений между к л ассами. IDL под д ерживает: • типы, определенные пользователем; • последовательности, определенные пользователе м; • типы массивов; • рекурсивные типы; • семантику исключений; • модули (по аналогии с пространствами имен); • единичное и множественное наследование; • поразрядные арифметические операторы. Приведем IDL-определение для класса adding_machine из листинга 8.2: interface adding_machine{ void add(in unsigned long X); void subtract(in unsigned long X); long result(); } Это определение начинается с ключевого слова CORBA interface. Обратите внимание на то, что данное объявление интерфейса класса adding_machine не включает ни одной переменной, которая бы могла хранить результат выполнения операций сложения и вычитания. Методы add () и subtract () принимают в качестве параметра одно значение типа unsigned long. Объявление типа параметра сопровождается ключевым словом CORBA in, который говорит о том, что данный параметр является входным (mput). Это объявление класса хранится в отдельном исходном файле adding_machine.idl. Исходные файлы, содержащие ГОЬюпределения, должны иметь . idl-расширения. Прежде чем такой файл можно будет использовать, его необходимо преобразовать к С++чЈюрмату. Это преобразование можно выполнить с помощью препроцессора или отдельной программы. Все CORBA-реализации включают IDL-компиляторы. Существуют IDL-компиляторы лля языков С, Smalltalk, С++, Java и др. IDL-компилятор преобразует ГОЬюпределения в код соответствующего языка. В данном случае IDL-компилятор преобразует объявление интерфейса в легитимный C++-код. В зависимости от конкретной CORBA-реализации IDL-компилятор вызывается с использованием синтаксиса, который будет подобен слелующему: idl adding_machine.idl При выполнении этой команды создается файл, содержащий С++-код. Поскольку наше IDL-определение хранится в файле adding_machine. idl, MICO IDL-компилятор создаст файл adding_machine. h, который будет содержать несколько каркасных C++-классов и CORBA-типов данных. Базовые IDL-типы данных приведены в табл. 8.2. Таблица 8.2. Базовые IDL -т ипы дан н ых IDL-типы данных Диапазон Размер long 1 > _2»-2' 5 - 1 > 16 бит 0-2 v - 1 > 32 бит
Даже после того как IDL-компилятор создаст из определения интерфейса С++-код, реализация методов интерфейсного класса остается все еще неопределенной. IDL-компилятор создает несколько С++-конструкций, которые предназначены для использования в качестве базовых классов. В листинге 8.3 показано два класса, сгенерированных MICO IDL-компилятором на основе файла adding_machine.idl . // Листинг 8.3. Два класса, сгенерированные // MICO IDL-компилятором из файла // adding_machine.idl class adding_machine : virtual public CORBA::Object{ public: virtual ~adding_machine(); #ifdef HAVE_TYPEDEF_OVERLOAD typedef adding_machine_ptr _ptr_type; typedef adding_machine_var _var_type; #endif static adding_machine_ptr _narrow(CORBA::Object_ptr obj ); static adding_machine_ptr _narrow(CORBA::AbstractBase_ptr obj ); static adding_machine_ptr _duplicate(adding_machine_ptr _obj ){ CORBA::Object::_duplicate (_obj); return _obj; } static adding_machine_ptr _nil(){ return 0; } virtual void *_narrow_helper( const char *repoid ); static vector<CORBA::Narrow_proto> *_narrow_helpers; static bool _narrow_helper2( CORBA::Object_ptr obj ); virtual void add( CORBA::ULong X ) = 0; virtual void subtract( CORBA::ULong X ) = 0; virtual CORBA::Long result() = 0; protected: adding_machine(){}; private: adding_machine( const adding_machine& ); void operator=( const adding_machine& ); }; class adding_machine_stub : virtual public adding_machine{ public: virtual ~adding_machine_stub(); void add( CORBA::ULong X ); void subtract( CORBA::ULong X ); CORBA::Long result(); private: void operator=( const adding_machine_stub& ); }; Файл adding_machine.idl — это входные данные для компилятора, а файл adding_machine.h вместе с каркасны м и класса м и— результат его работы. Чтобы реализовать интерфейсы функций, объявленные в исходно м IDL-файле, разработчик использует наследование. Напри м ер, в листинге 8.4 представлен определенный пользователе м класс, который обеспечивает реализацию для одного из каркасных классов, созданных IDL-ко м пиляторо м. // Листинг 8.4. Класс реализации структурных классов, // созданных IDL-компилятором class adding_machine_impl : virtual public adding_machine_skel { private: CORBA::Long Result; public: adding_machine_impl (void){ Result = 0; }; void add(CORBA::ULong X){ Result = Result + X; }; void subtract(CORBA::ULong X){ Result = Result - X; }; CORBA::Long result(void){ return(Result); }; Один из каркасных файлов, созданных IDL-ко м пилятором из интерфейсного клас са adding_machine, называется adding_machine_skel. Обратите внимание на то, что при выведении новых классов IDL-компилятор берет имя из определения интерфейса. Наш класс adding_machine_impl обеспечивает реализацию интерфейса функций, объявленного с использованием языка IDL. Во-первых, в классе adding_machine_impl объявляется член данных Result. Во-вторых, здесь предлагается реализация методов add(), subtract () и result (). В то время как интерфейсный класс adding_machine включает объявление этих методов, класс adding_machine_impl обеспечивает их реализацию. Определяемый пользователем класс adding_machine_impl должен наследовать из базового класса множество функций, полезных для распределенного программирования. В этом и состоит основная схема работы, связанной с CORBA-программированием. Интерфейсный класс предназначен для представления используемых интерфейсов. Назначение IDL-компилятора ~ сгенерировать реальные каркасные С++-классы, исходя из определения интерфейса. Разработчик выводит класс из одного каркасных и обеспечивает реализацию методов, определенных в интерфейсном классе, и членов данных, которые должны использоваться для хранения атрибутов объекта. Итак, создание реальных С++-классов из IDL-определения представляет собой процесс, состоящий из трех действий. 1. Проектирование интерфейсов классов, отношений и иерархии с использование м языка IDL. 2. Использование IDL-ко м пилятора д ля генерирования реальных каркасных C++-классов на основе IDL-классов. 3. Использование наследования для создания пото м ков из одного из нескольких каркасных классов и реализация м етодов интерфейса, унаследованных от каркасных классов. Мы рассмотрим этот процесс более детально ниже в этой главе. Но сначала познакомимся с базовой структурой программы потребителя. Анатомия базовой CORBA-программы потребителя Одной из самых распространенных моделей для применения распределенного программирования является модель «изготовитель-потребитель». В этой модели одна программа играет роль «изготовителя», а другая — «потребителя». Изготовитель создает некоторые данные или предлагает ряд услуг, которыми пользуется потребитель (например, наша программа могла бы по требованию генерировать уникальные номерные знаки). Предположим, потребитель — это программа, которая создает запросы на новые номерные знаки, а изготовитель — это программа, которая их генерирует. Обычно потребитель и изготовитель размещаются в различных адресных пространствах. Компоненты такой программы и действия, которые должно содержать большинство CORBA-программ потребителей, представлены на рис. 8.4. Для взаимодействия с объектами, выполняемыми на других компьютерах или расположенными в других адресных пространствах, каждая программа— участница взаимодействия должна объявить ORB-объект. После этого программа-потребитель может получить доступ к его функциям-членам. Как показано на рис. 8.4, ORB-объект инициализируется путем следующего вызова:
При выполнении этой инструкции ORB-oбъект инициализируется. Для ORB-объектов используется тип CORBA: :ORB_var. В CORBA-реализациях объекты, тип которых помечается суффиксом _var, берут на себя заботу об освобождении базовой ссылки (в отличие от объектов, тип которых помечается суффиксом _ptr). Аргументы командной строки передаются конструктору ORB-объекта вместе с идентификатором orb_id. В данном случае идентификатором orb_id служит строка «mico-local-orb». Строка, передаваемал функции инициализации ORB_init (), зависит от конкретной CORBA-реализации. Полученный объект называют обслуживающим ( servant object ). После инициализации ORB-объекта и объектного адаптера разработчику CORBA-приложения необходимо позаботиться об IOR-ссылке для удаленного объекта (объектов). Как показано на рис. 8.4, IOR-ссылка считывается из файла adding_machine.ior . IOR-ссылка была записана в этот файл в строковой форме. ORB-объект используется для преобразования IOR-ссылки из строки снова в объектную форму с помощью метода string__to_object (). Как показано на рис. 8.4, это реализуется с помощью следующего вызова: CORBA::Object_var Obj = Orb->string_to_object(Ior.c_str()); Здесь функция lor. c_str () возвра щ ает IOR-ссылку в строковой форме, а объект Obj будет содержать IOR-ссылку в объектной форме. Объектнал форма IOR-ссылки затем претерпевает процесс «сужения», который подобен операции приведения типа в С++. В результате это г о процесса объектная ссылка приводится к соответствующему типу объекта. В данном случае «соответствую щ им» является тип adding_machine. Программа-потребитель (см. рис. 8.4) сужает IOR-объект, используя следующий вызов: adding_machine_var Machine = adding_machine::_narrow(Obj); При выполнении этой инструкции создается ссылка на объект типа adding_machine. Программа-потребитель м ожет теперь вызывать м етоды, определенные в IDL-интерфейсе для класса adding_machine, напри м ер: Machine->add(500); Machine->subtract(125) ; При выполнении этих инструкций вызываются м ето д ы add( ) и subtract () удаленного объекта. Несмотря на то что рассматриваемал программа-потребитель сильно упрощена, она дает представление о базовых компонентах типичных CORBA-программ потребителя или клиента. Однако программа-потребитель должна работать совместно с программой-изготовителем. Поэтому мы рассмотрим упрощенную CORBA-программу, которая действует как изготовитель для программы-потребителя, показанной на рис. 8.4. Анатомия базовой CORBA-программы изготовителя Изготовитель отвечает за обеспечение программ-потребителей данными, функциями или другими услугами. Изготовитель вместе с потребителем и составляют распределенное приложение. Каждал CORBA-программа изготовителя проектируется в расчете на существование программ-потребителей, которые булут нуждаться в предоставляемых ею услугах. Следовательно, каждая программа-изготовитель должна создавать обслуживающие объекты и IOR-ссылки, посредством которых к этим объектам можно получить доступ. На рис. 8.5 представлена простая программа-изготовитель, используемая «в содружестве» с программой-потребителем, отображенной на рис. 8.4. На рис. 8.5 также перечислены основные компоненты, которые должна содержать любая CORBA-программа изготовителя. Обратите внимание на то, что части А обеих программ по сути одинаковы. Как потребителю, так и изготовителю требуется ORB-объект для связи друг с другом. Этот ORB-объект используется для получения ссылки на объектный адаптер. На рис. 8.5 приведен следующий вызов: CORBA::BOA_var Boa = Orb->BOA_init(argc,argv,«mico-local-boa»); Итак, вызов этой функции используется для получения ссылки на объектный адаптер, который служит посредником между ORB-брокером и объектом, реализующим запрашиваемые методы. Слелует иметь в виду, что CORBA-объекты должны начинаться только как объявления интерфейсов. На некотором этапе процесса разработки производный класс обеспечит реализацию CORBA-интерфейса. Объектный адаптер действует как посредник между интерфейсом, с которым связан ORB-брокер. и реальными методами, реализованными производным классом. Объектные адаптеры используются для доступа к обслуживающим объектам и объектам реализации. Изготовитель (см. рис. 8.5) создает объект реализации в части В, используя следующий вызов:
При выполнении этой инструкции создается объект, который обеспечит реализацию услут, потенциально запрашиваемых клиентскими объектами (или потребителями). Обратите также внимание на то, что в части С (см. рис. 8.5) программа-изготовитель использует объект ORB для преобразования IOR-ссылки в строку и записывает ее в файл adding_machine.ior . Этот файл можно передать с помощью FTP-протокола, по электронной почте, посредством протокола передачи гипертекстовых файлов (HTTP) вместе с Web-страницами, с помощью сетевой файловой системы NFS и т.д. Существуют и другие способы передачи IOR-ссылок, но файловый метод — самый простой. После записи IOR-ссылки программа-изготовитель просто ожидает запросы от программ-клиентов (потребителей). Программа-изготовитель, представленная на рис. 8.5, также представляет собой упрощенный вариант CORBA-программы изготовителя (программы-сервера), тем не менее, она содержит все основные компоненты, которые должна иметь типичная программа-изготовитель. Базовый npoeкт CORBA-приложения Итак, из программ, представленных на рис. 8.4 и 8.5, видно, что д л я CORBA-при л ожения потребуются два ORB-объекта, объектный адаптер, метод передачи IOR-ссылки и по крайней мере один обслуживаю щ ий объект. Логическал структура CORBA-приложения показана на рис. 8.6. После получения IOR-ссылки и приведения ее к соответствующему типу вызов удаленного метода в программе клиента (потребителя) подобен вызову обычного метода в любой С++-программе. В CORBA-примерах этой книги предполагается использование протокола IIOP (Internet Inter ORB Protocol). Поэтому ORB-брокеры (см. рис. 8.6) связываются с помощью протокола TCP/IP. IOR-ссылка должна содержать информацию о местоположении удаленного объекта, достаточную для реализации TCP/IP-связи. В качестве объектного адаптера обычно используется переносимый объектный адаптер. Но для некоторых программ (более старых и простых) можно использовать базовый объектный адаптер. Различие между этими двумя адаптерами мы рассмотрим ниже в этой главе. Каждое CORBA-приложение имеет один или несколько обслуживающих объектов, которые реализуют интерфейс, разработанный в IDL-классе. Простейшие программы потребителя и изготовителя, показанные на рис. 8.4 и 8.5, могут выполняться на одном компьютере в различных процессах или на различных компьютерах. Если эти программы выполняются на одном компьютере, файл adding_machine. ior должен быть доступен из обеих программ. Если они выполняются на различных компьютерах, этот файл должен быть послан клиентскому компьютеру по FTP-протоколу, электронной почте, HTTP-протоколу и т.д. Детали компиляции и выполнения этих программ описаны в разделах «Профиль программы 8.1» и «Профиль программы 8.2».
IDL-компилятор IDL-компилятор представляет собой инструмент, предназначенный для перевода IDL-определений класса в С++-код. Этот код состоит из коллекции «каркасных» определений классов, перечислимых типов и шаблонных классов. Для CORBA-программ, приведенных в этой книге, в качестве IDL-компилятора используется MICO IDL-компилятор. В табл. 8.3 перечислены опции командной строки, которые чаще всего применяются при вызове этого IDL-компилятора. Таблица 8.3. Самые распространенные опции командной строки, применяемые при вызове IDL-компилятора • --boa Генерирует «каркасные» конструкции, которые используют базовый объектный адаптер (basic object adapter — BOA). Эта опция испо л ьзуется по умо л чанию • --no-boa Отключает генерирование кода «каркасных» конструкций для BOA • --poa Генерирует «каркасные» конструкции, которые испо л ьзуют переносимый объектный адаптер (portable object adapter — POA) • --no-poa Отк л ючает генерирование кода «каркасных» конструкций для POA. Эта опция используетс я по умолчанию • -- g en-included-defs Генерирует код, который был включен с по м о щ ью директив #include • —version Выводит версию спецификации MICO • -D<define> Опреде ля ет макрос препроцессора. Эта опци я эквивалентна ключу -D у большинства UNIX С-компиляторов • -I<path> Определяет путь поиска для директив #include. Эта опция эквивалентна к л ючу -I у бо л ьшинства UNIX С-компи ля торов Ключи -boa и -poa (см. табл. 8.3) позволяют определить, на какой тип адаптера будут ориентированы создаваемые «каркасные» конструкции. Например, при выполнении слелующей команды idl -poa -no-boa adding_machine.idl будет получен файл adding_machine.h , который содержит «каркасные» конструкции для РОА-адаптера (portable object adapter), и будет отключено генерирование «каркасных» конструкций для ВОА-адаптера (basic object adapter). При вводе команды idl -h будет сгенерирован полный список ключей IDL-компилятора. Если в поставке MICO надлежащим образом инсталлированы man-страницы, то ввод команды man idl обеспечит полное описание доступных IDL-ключей. Проектирование IDL-классов — первый шаг в CORBA-программировании. На слелующем этапе необходимо определить способ хранения и считывания IOR-ссылок на удаленные объекты. Получение IOR-ссылки для удаленных объектов ORB-класс содержит две функции-члена (string_to_object() и object_to_. string ()), которые можно использовать для преобразования IOR-объектов из строк в объекты типа Object_ptrs и обратно. Функция-член string_to_object() принимает параметр типа const char * и преобразует его в объект типа Object_ptr. Функция-член Object_to__string() принимает параметр типа Object_ptr и преобразует его в указатель типа char *. Эти методы являются составной частью интерфейса ORB-клаеса. Метод Object_to_string () используется для получения объектной IOR-ссылки строковой формы. IOR-ссылку, представленную в виде строки, можно передать программам клиентов (потребителей) различными способами, например: • Электронная почта • Разделяемые файловые системы (NFS-оборудование) • FTP-протокол • Встраивание в HTML-документы • Java-аплеты/сервлеты • Аргументы командной строки • Разделяемая память • Традиционные средства межпроцесной связи (IPC), т.е. каналы, FIFO-очереди и пр. • Переменные среды CGI-команды приема и отправки данных Затем программа приема данных получает строковый вариант IOR-ссылки и использует функцию-член string_to_object () ORB-объектадля преобразования IOR-ссылки в CORBA-объект ptr. Этот CORBA-объект ptr затем «сужается» (т.е. приводится к соответствующему типу) и используется для инициализации локального объекта. В программах 8.1 и 8.2 для передачи IOR-ссылки между программой-потребителем и программой-изготовителем используются строковые формы объектов и файл. Строковую форму IOR-ссылок можно использовать для обеспечения очень гибкой связи с удаленными объектами, которые могут размещаться практически в любом месте Internet, intranet или extranet. Существует реализация MIWCO (Wireless Mico) — открытая реализация спецификации wCORBA [16], стандарт беспроводной спецификации CORBA, который можно использовать для улучшения мобиль-ности объектов. Эта беспроводнал спецификация позволяет реализовать связь посредством MIOR-ссылки (Mobile IOR). Она поддерживается для TCP-, UDP- и WAP WDP-механизмов передачи (Wireless Application Protocol Wireless Datagram Protocol). Мультиагентные и распределенные агентные системы также могут воспользоваться преимуществами стандартов IOR-ссылок. IOR- и MIOR-ссылки являются частью «строительных блоков» слелующего поколения объектно-ориентированных Web-служб. Важно отметить, что хотя строковые IOR-ссылки обеспечивают гибкость и переносимость, они не могут идеально подходить ко всем ситуациям и конфигурациям. Передача файла, который содержит IOR-ссылку, — не слишком приемлемое требование для многих систем. Трудно с точки зрения практичности требовать от приложений клиента и сервера разделять одну и ту же файловую систему или сеть. А с точки зрения безопасности строковый вариант IOR-ссылки вообще исключается как достойный рассмотрения. Если приложение типа «клиент-сервер» велико по объему и достаточно разнотипно, то требование разделения строковой формы IOR<cbmKH может оказаться слишком ограничивающим. CORBA-спецификация включает еще два стандарта для получения или передачи объектных ссылок: службы имен и маклерские службы. Служба имен Стандарт службы имен обеспечивает механизм преобразовани я имен в объектные ссылки. Автор запроса на IOR-ссылку предоставляет в службу имен имя, а она возвращает ему объектную ссылку, соответствующую этому имени. Служба имен действует как разновидность телефонного справочника, в котором по имени и щ ется номер. Эта служба позволяет программам клиента (потребителя) находить по имени объектные ссылки. Эту службу можно использовать не только для получения IOR-ссылок, но и для других ресурсов приложения. Получение объектной ссылки по имени называется связыванием по имени (name binding). Коллекция вариантов связывания по имени соот н осится с объектом именного контекста (naming context). Чтобы проиллюстрировать понятие именного контекста, предположим, что у нас есть приложение, которое предназначено для планировани я маршрутов и состоит из большого количества различных объектов. Мы можем организовать группы объектов в соответствии с выполняемы м и и м и функция м и. Одни объекты относятся к группе файлового ввода-вывода данных, а другие — к группе безопас н ости. Наше приложение использует также объекты, которые имеют отношение к видам транспорта: поезд, автобус, автомобиль и велосипед. Каждое такое группирование образует некоторый контекст. Например, чтобы логически сгруппировать объекты, связанные с видами транспорта, можно создать контекст транспорта и связать с ним все виды транспорта. Такое группирование позволяет сформировать именной контекст. Сначала связываем наименование каждого вида транспорта с его IOR-ссылкой. Это и есть связывание по имени. Затем соотносим это связывание по имени с контекстом транспорта. Мы используем контексты для логической организации групп связанных объектов. Коллекция связанных именных контекстов образует именной граф (naming graph). Именные контексты представляются объектами. Поскольку именной контекст реализуется как объект, он может участвовать в связывании по имени подобно любому другом)' объекту. Это означает, что именной контекст может потенциально содержать другие именные контексты. Например, на рис. 8.7 показано несколько контекстов, включающих логическое представление нашего контекста транспорта. Обратите внимание на то, что пос л едняя строка в контексте транспорта представляет собой имя airborne (возлушный вид транспорта). Для имени airborne существует еще один контекст, именуемый flying_machines (летательные аппараты). Контекст flying_machines содержит связи с неско л ькими объектами (на основе выполняемыхфункций). Контекст transportation (транспортные средства) вместе с контекстом flying_machines образуют именной граф. На рис. 8.7 обратите внимание на то, что последний объект в контексте flying_machines называется sonic (звуковые). Имя sonic связано с контекстом fast_flying_machines. Имя sonic имеет объектную ссылку 8888. Тем самым к именному графу добавляется еще один контекст. Это — пример именного контекста, содержащего другой именной контекст. Именной граф можно использовать для представления «многоплановой» структуры взаимосвязей в распределенном объектно-ориентированном приложении. Можно сказать, что именной граф фиксирует панораму распределенного приложения. Для мультиагентных систем именной граф можно использовать в качестве разновидности семантической сети (см. § 8.1). Несмотря на то что объекты могут быть разбросаны по различным аппаратным платформам, операционным системам, языкам программирования и географически отдаленным компьютерам, именной граф может представлять единую логическую структуру взаимоотношений и связей между объектами. На рис. 8.8 показано альтернативное представление именного графа, приведенного на рис. 8.7. Именные контексты этих двух рисунков совпадают, и в обоих случаях отчетливо отображены взаимоотношения между именными контекстами. На рис. 8.8 также показано, что существует путь от контекста transportation к контексту fast_flying_machines и обратно к контексту transportation. Для обхода именного графа в процессе решения распределенной задачи применяются известные алгоритмы обхода графов. При этом различные пути обхода именного графа могут представлять различные решения задачи. Служба имен обеспечивает автора запроса доступом к именным контекстахм и именным графам. К именным контекстам доступ осуществляется через именные графы, а к связям — через именные контексты. Связывание обеспечивает прямое соответствие имени и объектной ссылки. Рассмотрим программу 8.3, в которой представлен простой вариант «изготовителя», создающего связывание по имени и соотносящего это связывание с некоторым именным контекстом.
// Программа 8.3 1 #include <iostream> 2 #include <fstream> 3 #include «permutation_impl.h» 4 #define MICO_CONF_IMR 5 #include <CORBA-SMALL.h> 6 #include <iostream.h> 7 #include <fstream.h> 8 #include <unistd.h> 9 #include <mico/CosNaming.h> 10 11 12 int main(int argc, char *argv[]) 13 { 14 CORBA::ORB_var Orb = CORBA: :ORB_init(argc,argv,«mico-local-orb»); 15 CORBA::Object_var PoaObj =Orb->resolve__initial_references(«RootPOA»); 16 PortableServer::POA_var Poa =PortableServer::POA::_narrow(PoaObj); 17 PortableServer::POAManager_var Mgr =Poa->the_POAManager(); 18 inversion Server; 19 PortableServer: :ObjectId_var Oid =Poa->activate_object(&Server); 20 Mgr->activate () ; 21 permutation_ptr ObjectReference = Server.__this(); 22 CORBA::Object_var NameService =Orb->resolve_initial_references («NameService»); 23 CosNaming: :NamingContext_var NamingContext =CosNaming::NamingContext::_narrow (NameService); 24 CosNaming: :Name name; 25 name.length (1) ; 26 name[0].id = CORBA::string_dup («Inflection»); 27 name[0].kind = CORBA::string_dup (""); 28 NamingContext->bind (name, ObjectReference); 29 Orb->run(); 30 Poa->destroy(TRUE,TRUE); 31 return(0) ; 32 } 33 34 § 8.1. Семантические сети Семантическал сеть (semantic network) — это одна из са м ых старых и простых схе м представления зна н ий. В ос н ове се м а н тической сети лежит г рафическое изображение иерархических взаи м оот н ошений между объекта м и. На рис. 8.9 показа н а простая семантическая сеть, которая отображает зна н ия о тра н спортных средствах в цело м и о конкретных транспортных средствах в част н ости.
Овалы в семантической сети называются узлами, а линии — связями. Связи представляют существующие отношения между узлами. Узлы используются для представления объектов и фактов (или описателей). Некоторые связи являются дефинициональными, а другие могут быть вычислены. Связи можно использовать для отображения наследования или подчиненности. Узлы и связи вместе выражают некоторые порции знаний. Например, изучив семантическую сеть, представленную на рис. 8.9, мы понимаем, что F-15 — это транспортное средство, а также летательный аппарат, который имеет по крайней мере два крыла. Семантические сети используются для представления знаний, необходимых в ПО принятия решений. Использование службы имен и создание именных контекстов При выполнении строки 22 серверная про г рамма получает ссылку на службу имен CORBA::Object_var NameService =Orb->resolve_initial_references(«NameService»); Помимо получения объектных ссылок на хранилище реализаций (Implementation Repositoiy) и хранилище интерфейсов (Interface Repositoiy), метод ORB-объекта resolve_initial_references() используется д л я получени я ссылки на службу имен. Получив нужную ссылку, программа-сервер создает на ее основе именной контекст (см. строку 23): CosNaming::NamingContext_var NamingContext = CosNaming::NamingContext::_narrow(NameService); При таком подходе мы получаем начальный именной контекст, который играет роль контекста, действующего по умолчанию. Обнаружив службу имен и создав начальный именной контекст, серверная программа может добавлять в контекст пары (связывания по имени) «имя/объектнал ссылка». Имена могут представлять собой объекты доменов или другие контексты. Чтобы добавить в контекст пару «имя/объектная ссылка», необходимо сначала создать имя. Имена реализуются в стандарте CORBA посредством структуры NameComponent. struct NameComponent { //.. . Istring_var id; Istring_var kind; } В CORBA-реализации MICO структура NameComponent объявляется в файле CosNaming. h. Структура NameComponent содержит два атрибута: id и kind. Первый атрибут используется для хранения текста имени, а второй представляет собой идентификатор, который можно использовать для классификации объекта, например так. //... CosNaming::Name ObjectName; ObjectName.length(1); ObjectName.id = Corba::string_dup (" train») ; ObjectName.kind=Corba::string_dup(«land_transportation»); NamingContext->bind(ObjectName,ObjectReference) ; //... Здесь объяв л яется объект типа NameComponent. Атрибут id устанавливается равным значению «train», а атрибут kind— значению land_transportation. Очевидно, атрибут id до л жен быть описате л е м (дескриптором) объекта. Атрибут kind м ожно ис-пользовать для описания контекста или логической группы, к которой принадлежит этот объект. В данном случае он классифицирует поезд (train) как объект land_transportation (назе м ный вид транспорта). Метод bind () преобразует и м я объекта ObjectName в объектную ссылку ObjectReference и связывает ее с начальны м именным контексто м. И м я м ожет состоять из нескольких объектов типа NameComponent. Если имя состоит только из одного объекта типа NameComponent, оно называется простым, а если из нескольких — составным. Если имя составное, то атрибут kind можно использовать для описания отношения (этот метод рассматривается в главе 12). В программe 8.3 объект связывается с объектной ссылкой, которая соотносится с именованным контекстом. После связывания с именным контекстом объект клиента может получить доступ к контексту посредством службы имен. В программах 8.1 и 8.2 для связи (посре д ство м строковой IOR-ссылки ) между программами потребителя и изготовителя мы использовали файл. А для связи клиента и сервера (см. программу 8.3) используется служба имен. Детали инсталляции и функционирования службы и м ен зависят от конкретной реализации. Среда MICO включает программу nsd, которая реализует COS-сов м ести м ую службу имен. Прежде чем служба имен будет доступной для программы-погребителя, необходимо залустить де м он micod и внести соответствующие элементы в хранилище реализаций. Чтобы узнать, как пользоваться програ мм а м и nsd, micod и imr, обратитесь к соответствую щ ей доку м е н тации и руководству по MICO (о н о содержит м ножество примеров использования программ imr, nsd, micod и ird). В листинге 8.5 приведен фрагмент из сценария, используемого для настройки сервера в программе 8.3, позволяющей сделать службу имен доступной для программы-потребителя. // Листинг 8.5. Сценарий внесения записи в хранилище // реализаций и запуска службы имен // micod -ORBIIOPAddr inet:hostname:portnumber —forward & imr create NameService poa 'which nsd* IDL:omg.org/CosNaming/ NamingContext:1.0#NameService \ -ORBImplRepoAddr inet:hostname:portnumber \ -ORBNamingAddr inet:hostname:hostname:portnumberportnumber imr create permutation persistent "'pwd'/permutation_server \ -ORBImplRepoAddr inet:hostname:portnumber \ -ORBNamingAddr inet:hostname:portnumber» IDL:permutation:1.0 \ -ORBImplRepoAddr inet:hostname:portnumber \ -ORBNamingAddr inet:hos tname:portnumber imr activate permutation -ORBImplRepoAddr inet:hostname:portnumber \ -ORBNamingAddr inet:hostname:portnumber Этот сценарий можно использовать в сочетании с кодом сервера, приведенным в программе 8.3. Приведенный здесь сценарий реально позволяет автоматически запустить программу-сервер permutation_server. Обратите вни м ание на то, что имена hostname и portnumber в программе 8.5 необходимо заменить реальным именем компьютера, на котором выполняется сервер, и номером порта соответственно. Служба имен «потребитель-клиент» Программа 8.3 связывает имя объекта с именным контекстом. Программа 8.4 содержит текст программы-потребителя, которая использует службу имен для доступа к связкам «объект-ссылка», которые были созданы в программе 8.3. Программа 8.3 генерирует перестановки любой строки символов, которую она получает. Для перестановки изменяется местоположение символов в строке. Например, эти строки Objcte JbOetc tbOjec Ojbect JObetc Ojbcet JtObec представ л яют собой перестановки строки Object. К л иент передает серверу строку и сервер генерирует N перестановок. Сервер связывает и м я «Inflection» с именным контекстом. Именно это имя программа-клиент должна задать, чтобы получить объектную ссылку из именного контекста. // Программа 8.4 1 int main(int argc, char *argv[]) 2 { 3 4 try{ 5 CORBA::ORB_var Orb = CORBA::ORB_init(argc,argv,«mico-local-orb»); 6 object_reference Remote(«NameService»,Orb); 7 Remote.objectName(«Inflection»); 8 permutation_var Client =permutation::_narrow(Remote.objectReference()); 9 char Value[1000]; strcpy(Value,«Common Object Request Broker»); 11 Client->original(Value); 12 int N; 12 for(N = 0;N < 15;N++) 14 { 15 cout « «Значение функции nextPermutation() "<< Client- >nextPermutation() « endl; 16 ) 17 } 18 catch(CosNaming::NamingContext::NotFound_catch &exc) { 19 cerr << " Исключение: объект не обнаружен.» « endl; 20 } 21 catch(CosNaming::NamingContext::InvalidName_catch &exc) { 22 cerr << «Исключение: некорректное имя.» « endl; 23 } 24 25 return(0); 26 } Для доступа к соответствую щ ему объекту и м енного контекста в програ мм е-потребителе необхо д и м о выполнить следующие три д ействия. 1. Получить ссылку на службу имен. 2. С помощью службы имен получить ссылку на соответствующий именной контекст. 3. С помощью именного контекста получить ссылку иа соответствующий объект. Действие 1 реализуетс я путе м вызова м ето д а resolve_initial_references(): //.. . CORBA::Object_var NameService; NameService = Orb->resolve_initial_references («NameService»); //... Функция resolve_initial_references () возвратит объектную ссылку на службу имен. В действии 2 эта ссылка используетс я дл я получения объектной ссылки на именной контекст: CosNaming: :NamingContext_var NameContext; NameContext = CosNaming::NamingContext::_narrow (NameService); В действии 3 значение объектной ссылки NameService «сужается», т.е. приводится к соответствую щ ему типу, в результате чего получаетс я объектнал ссылка на и м енной контекст NameContext. С по м ощью объекта NameContext програ мм а-потребитель может вызвать его м етод resolve(). Строки 24-27 из програ мм ы8.3 используютс я дл я построени я и м ени, которое и будет передано м етолу resolve (). Name .length (1); Name[Q].id = CORBA::string_dup («Inflection»); Name[C].kind = CORBA::string_dup (""); try { ObjectReference = NameContext->resolve (Name); } Метод resolve() возвращает объектную ссылку, связанную с заданным именем объекта. В данном случае задано имя «Inflection». Обратите внимание на то, что такое же имя связывается с именным контекстом в программе8.3 (строка28). Если программа-потребитель имеет объектную ссылку, она может ее «сузить», а затем с ее помощью получить доступ к удаленному объекту. Процесс получения объектной ссылки на удаленный объект вполне тривиален, и поэтому имеет смысл его упростить путем инкапсуляции соответствующих компонентов в классе. class object_reference{ //.. . protected: CORBA::Object_var NameService; CosNaming::NamingContext_var NameContext; CosNaming::Name Name; CORBA::Object_var ObjectReference; public: object_reference(char *Service,CORBA::ORB_var Orb); CORBA::Object_var objectReference(void); void objectName(char *FileName,CORBA::ORB_var Orb); void objectName(char *OName); //. . . } Про г рамма 8.4 использует преи м у щ ества просто г о каркасно г о к л асса obj ect_reference, который м ы создали с этой целью. В про г ра мм е 8.4 (строка 6) обратите вни м ание на создание объекта Remote типа object_reference. В строке 8 этот объект используется для получения ссылки на удаленный объект с помощью следующего вызова м етода: Remote.obj ectReference(); После этого программа-потребитель получает доступ к удаленному объекту. Класс object_reference обеспечивает выполнение некоторых необходимых действий и тем самым упрощает написание программы-потребителя. Ко н структор класса object_reference (о н вызывается в строке 6 программы 8.4) реализован следующим образом. object_reference::object_reference(char *Service, CORBA::ORB_var Orb) { NameService = Orb->resolve_initial_references (Service); NameContext = CosNaming::NamingContext::_narrow ( NameService); } Этот ко н структор получает ссылку на службу и м ен и создает объект к л асса NameContext. В строке 7 и м я это г о объекта передается м етолу objectName (). Затем для получения объектной ссылки, связанной с именем объекта, используется именной контекст. Метод objectName () реализован с л едую щ и м образо м. void object_reference::objectName(char *OName) { Name.length (1); Name[0].id = CORBA::string_dup (OName); Name[0].kind = CORBA::string_dup (""); try { ObjectReference = NameContext->resolve (Name); } catch(...) { cerr « " Problem resolving Name " « endl; throw; } } После вызова метода objectName() программа-потребитель получает доступ кссылке на удаленный объект. Теперь остается лишь вызвать метод objectReference() (это реализуется в строкев программы8.4). В методе objectName () основную часть работы выполняет функция resolve (). Программы 8.3 и 8.4 образуют простое распределенное приложение «клиент-сервер», которое для доступа к объектным ссылкам вместо строковой формы IOR-ссылок использует службу имен. В сетях intranet или Internet можно использовать оба подхода. Эти же варианты применяются в качестве опорных структурных компонентов в контексте новой модели Web-служб. Подробнее об объектных адаптерах Помимо службы имен и объекта именованного контекста, сервер в программе 8.3 также использует переносимый объектный адаптер. Вспомните, что адаптер (см. рис. 8.6) действует как своего рода посредник между ORB-брокером и обслуживающим объектом, который в действительности выполняет работу CORBA-объекта. Мы можем сравнить этот обслуживающий объект с «наемным» писателем, который пишет книту от имени «подуставшей» знаменитости. С этой знаменитостью наперебой общаются журналисты, литературные агенты и юристы. Знаменитость удостаивается всех почестей, но реальную работу делает за него другой человек. CORBA-объект «публикует» интерфейс с внешним миром и играет роль «знаменитости» в CORBA-программе. Программа-клиент (или потребитель) взаимодействует с интерфейсом, который обеспечивает CORBA-объект, но реальную работу выполняет обслуживающий объект, играя роль «наемного» писателя. Обслуживающий объект имеет собственный протокол, который может отличаться от используемого CORBA-объектом. CORBA-объект может предоставить С++-интерфейс для связи склиентом. Обслуживающий объект может быть реализован на Java, Smalltalk, Fortran и других языках программирования. Объектный адаптер обеспечивает интерфейс с обслуживающим объектом. Он адаптирует этот интерфейс, чтобы реализация обслуживающего объекта была прозрачна для ORB-брокера и программы-клиента. CORBA-реализация должна нормально поддерживать два типа объектных адаптеров: Basic Object Adapter (BOA) и Portable Object Adapter (POA). Сначала стандарт CORBA был ориентирован на использование ВОА-адаптера, но он не был достаточно гибким. Поэтому и был разработан РОА-адаптер, который нашел более широкое применение. ВОА-адаптер обладает минимальным набором средств, но его вполне можно использовать для активизации объектных реализаций на базе информации, которая содержится в хранилище реализаций (табл. 8.4). Таблица 8.4. Некоторые элементы, содержащиеся в хранилище реализац и й Имя объекта Уникальный идентификатор для каждого объекта Режим активации Разделяемый, неразделяемый, постоянный, биб- лиотека permethod Путь Имя или путь выполняемого файла Список идентификаторов хранилища ВОА-адаптер, чтобы приступить к выполнению объекта изготовителя (сервера), использует такие записи из хранилища реализаций, как режим активизации и путь. И хотя в ряде более простых примеров, приведенных в этой главе, используется ВОА-адаптер, мы рекоменлуем для серьезной CORBA-разработки применять РОА-адаптер. РОА-адаптер поддерживает: • прозрачную активизацию объекта; • транзитные объекты; • не я вную активизацию обслуживаю щ их объектов; • перманентные (постоянные) объекты за пределами сервера. Возможно, наиболее важной функцией РОА-адаптера является взаимодействие собслуживающими объектами. CORBA-спецификация определяет обслуживающий объект следующим образом. Обслуживающий объект — объект языка программирования, который реализует запросы к одному или нескольким объектам. Обслуживающие объекты в общем случае существуют в контексте процесса сервера. Запросы на получение объектных ссылок обслуживаются ORB-брокером, действующим в качестве связующего звена, и трансформируются в вызовы конкретных обслуживающих объектов. Во время своего жизненного цикла объект может быть связан с несколькими обслуживающими объектами. Каждый обслуживающий объект должен иметь по крайней мере один POA-адаптер. Но возможны и другие конфигурации (рис. 8.10). РОА-адаптерами управляют специальные объекты управления, или POA-менеджеры. CORBA-спецификация определяет РОА-менеджер таким образом: РОА-менеджер — это объект, который инкапсулирует состояние обработки одного или нескольких РОА-адаптеров. С помощью РОА-менеджера разработчик может сделать запросы к соответствующим РОА-адаптерам с организацией очереди. Разработчик также может использовать РОА-менеджер для дезактивизации РОА-адаптеров. Сервер в программе 8.3 служит простым примером использования объектов POA- адаптеров и РОА-менеджеров. Более подробное рассмотрение POA выходит за рамки нашей книги. За деталями обращайтесь к работе [20]. MICO-поставка также содержит ряд примеров использования мощных средств POA.
Хранилища реализаций и интерфейсов ORB-брокер для определения местоположения объектов, когда строковые IOR-ссылки недоступны, использует хранилище реализаций. Хранилища реализаций представляют собой удобное место для хранения информации, связанной со спецификой среды (например, данные о системе безопасности, детали отладки и т.д.). Хранилище реализаций должно содержать информацию, достаточную для того, чтобы ORB-брокep мог отыскать путь объекта и выполняемый файл. Утилита imr в поставке MICO используется для управления хранилищем реализаций. Она позволяет отображать записи хранилища реализаций, вносить в него новые и удалять ненужные, например: imr create permutation persistent "'pwd' /permutation_server \ -ORBImplRepoAddr inet:hostname:portnumber \ -ORBNamingAddr inet:hostname:portnumber» IDL:permutation:1.0 \ При выполнении этой ко м анды в хранилище будет добавлена запись с перестановкой имени. Местоположение выполняе м ого файла определяется как 'pwd'/permutation_server . Эта запись также включает значения hostname и portnumber, которые сооб щ ают, где должна выполняться программа. Хранилище реализаций — весьма подходящее место для хранения информации этого типа об объекте. Эта запись также устанавливает режим активизации (persistent). ORB-брокер использует эту информацию для надлежащей инициализации выполнения программы с именем permutation_server. Полный список опций, доступных для программы imr, содержится в соответствующей документации. Помимо хранилища реализаций, используется хранилище интерфейсов, которое предназначено для хранения динамической информации о каждом объекте. Хранилище интерфейсов можно использовать для динамического определения местоположения интерфейса с CORBA-объектами. Для работы с хранилищем интерфейсов в MICO-реализациях CORBA-спецификации используется программа ird. И хотя CORBA-спецификация описывает логические возможности хранилищ реализаций и интерфейсов, все же некоторые ее детали (например, способы записи данных в хранилища и управления ими) зависят от конкретной среды, поставки и производителя. Простые pacnpeделенные Web-службы, использующие CORBA-спецификацию Адреса для хранилищ реализаций и служб имен можно встроить в код HTML и использовать как часть CGI-обращения к Web-серверу. Этот метод с помощью CORBA-спецификации можно применить для реализации простых распределенных Web-служб. В листинге 8.6 представлен простой HTML-код. При щелчке на гиперссылке будет выполнен CORBA-клиент. Этот CORBA-клиент может затем получить доступ к серверу, используя адрес хранилища реализаций и службы имен, который был передан в CGI-команде HTML-кода. // Листинг 8.6. HTML-документ со встроенным обращением к // CORBA-программе (клиенту) <HTML> <HEAD> <TITLE> CORBA</TITLE> </HEAD> <BODY> <a href=«http://www.somewhere.org/cgi-bin/client?-ORBImplRepoAddr+inet:hostname:port+-ORBNamingAddr+inet:hostname:port»>Click</a> <P> </HTML> Здесь клиент ссылается на программу, которая должна получить доступ к CORBA-изготовителю (серверу). У клиента есть имя объекта, с которым ему необходимо связаться, а для дальнейших действий он использует службу имен. Этот метод не требует загрузки кода на компьютер пользователя. Совсем наоборот, код клиента, выполняясь на Web-сервере, должен получить доступ к CORBA-ориентированной программе-серверу независимо от ее местоположения (или в intranet-сети, подключенной к Web-серверу, или где-нибудь в другом месте Internet). Программа-клиент должна ответить HTML-браузеру, используя соответствующий CGI-протокол. Простая конфигурация Web-служб с CORBA-компонентами показана на рис. 8.11. Помимо протокола http, для запуска CORBA-ориентированных клиентов и серверов можно использовать сетевой теледоступ telnet. Протоколы http и telnet можно использовать для поддержки глобального распределения CORBA-компонентов. При проектировании распределенных компонентов, ориентированных на функционирование в сети Internet или intranet, важно не забывать о системе безопасности (соответствующем ПО и данных). И хотя реализации и требования, предъявляемые к безопасности, выходят за рамки этой книги, мы подчеркиваем их важную роль в любом проекте распределенной системы. Для хранения информации, имеющей отношение к безопасности, можно использовать хранилище реализаций. Любую CORBA-реализацию можно использовать в сочетании с протоколом защищенных сокетов (Secure Socket Layer — SSL) и специальной оболочкой SSH (Secure Shell).
Маклерская служба Помимо строковых IOR-ссылок и службы имен, CORBA-спецификация включает более прогрессивный и динамический метод получения объектных ссылок, име-куемый маклерской службой (trading service). Эта служба пре д лагает более интересный (по сравнению с рассмотренными выше) подход к взаимодействию с удаленными объектами. Вместо взаимодействия со службой имен, клиент общается с «маклером», у которого доступ к объектным ссылкам организован практически так же, как и в службе имен. Однако «маклер» связывает с объектными ссылками не простые имена, а описания и интерфейсы. В то время как служба имен содержит пары «имя-ссылка», маклерская служба содержит пары «описания (интерфейсы)-ссылка». Клиенты могут описать для «маклера» объект, который им нужен, и тот ответит им соответствующей объектной ссылкой, если искомое соответствие будет обнаружено. Это — очень мощный поисковый метод. При этом клиент может не знать не только местоположения объекта, но и даже его имени. Это позволяет кли-енгу делать запросы на основе списка услуг, которые ему нужны, а не искать конкретный объект (запросы чего-то, которые не важно, где и кем будут удовлетворены). CORBA-спецификация определяет «маклер» следующим образом. Маклер — это объект, который поддерживает маклерскую службу в распределенной среде. Его можно представить себе в виде объекта, посредством которого другие объекты могут информировать о своих возможностях и сопоставлять свои потребности с объявленными (разрекламированными) возможностями. Рекламирование возможностей или услуг называется «экспортом». Совпадение с ними потребностей или обнаружение нужных услуг называется «импортом». Экспорт и импорт позволяют динамически обнаружить необходимые услуги и реализовать позднее связывание. И точно так же, как при связывании нескольких именных контекстов формируются именные графы, при связывании нескольких маклеров формируются маклерские графы. Именные и маклерские графы — это мощные методы представления знаний и функциональных возможностей. Именные и маклерские графы обеспечивают функционирование глобальных Web- и telnet-служб. Обход именных и маклерских графов может включать участки, которые потенциально имеют «ответвления» в какую-нибудь локальную сеть, intranet, extranet или Internet. Подобно именным контекстам маклеры обычно представляют определенные типы объектов. Например, мы могли бы позаботиться о том, чтобы маклеры одного типа имели доступ к объектам кредитных карточек, а маклеры другого — к объектам сжатия и шифрования. Можно создать маклеры, которые бы занимались объектами погоды и географии. А еще мы могли бы «научить» маклеры интересоваться финансовой деятельностью и страхованием. Объединив все эти маклеры, получим маклерский граф. Если один маклер будет работать от имени других, мы получим то, что можно назвать федерацией маклеров. Когда клиент описывает одному маклеру услуги, в которых он нуждается, а затем маклер общается со своими коллегами, чтобы найти эти услуги, то клиент и этот маклер включаются в федерацию маклеров. Это — самая мощная и гибкая форма «запроса, который не важно, где и кем будет удовлетворен». Когда федерация маклеров возвратит объектную ссылку, может оказаться, что она «родом бог-знает-откуда» и может быть реализована обслуживающим объектом (объектами), операционная система и язык программирования которого совершенно чужд программе клиента. Федерация маклеров обеспечивает доступ к очень большим и разнообразным коллекциям услуг. Следует иметь в виду, что CORBA-стандарт включает беспроводную спецификацию wCORBA, используемую для разработки мобильных агентных и мультиагентных систем. На рис. 8.12 показана базовая архитектура CORBA-ориентированного приложения типа «клиент-сервер», которое делает запросы к маклерам. Програ мм а-клиент может взаимодействовать с маклером (или маклерами) напрямую или косвенно через федерацию маклеров. На рис. 8.12 обратите внимание на то, что после получения объектной ссылки осуществляется взаимодействие с ORB-брокером. Термины, связанные с те м ой програ мм ирования м ак л еров, приведены в таб л. 8.5.
Таблица 8.5. Термины, связанные с темой программирования маклеров • Экспортер Рекламирует услугу с помощью маклера. Экспортер может быть провайдером услуг или анонсировать услугу от имени кого-то другого • Импортер Использует маклер для поиска услуг, соответствующих некоторому критерию. Импортер может быть потенциальным клиентом услуг или импортировать услугу от имени кого-то другого • Предложение Содержит описание анонсируемой услуги. Описание состоит из имени-услуги и типа услуги, объектной ссылки и свойств объекта Парадигма «клиент-сервер» Термины «клиент» и «сервер» часто применяются к различным видам программных приложений. Парадигма «клиент-сервер» состоит в разделении работы на две части, представляемые процессами или потоками. Одна часть, клиент, создает запросы на получение данных либо действий. Другая часть, сервер, выполняет эти запросы. Роли запрашивающей и отвечающей стороны в большинстве случаев определяются логикой самих приложений. Термины «клиент-сервер» используются на уровне операционной системы для описания отношений типа «изготовитель-потребитель», которые могут существовать между процессами. Например, если для взаимодействия двух процессов используется FIFO-очередь, один из процессов «играет» роль сервера, а другой — роль клиента. Иногда клиент может «исполнить» роль сервера, если сам будет получать запросы. Аналогично сервер будет выступать в роли клиента, если ему потребуется обращаться с запросами к другим программам. Конфигурация «клиент-сервер» — основнал архитектура распределенного программирования. При этом тип сервера обычно характеризует все приложение. Некоторые наиболее популярные типы программных серверов перечислены в табл. 8.6. Таблица 8.6. Основные типы программных серверов • Сервер приложений Используется для обеспечения множества клиентов доступом к приложению. Вся работа приложения делится между клиентом и сервером, причем большая ее часть делается на сервере, а клиент (имея собственный процессор) выполняет только некоторую часть работы • Файловый сервер Действует как центральное хранилище для разделяемых документов, мультимедийных файлов, баз данных и т.д. Клиенты обычно представлены терминалами или рабочими станциями в сети. Клиент делает запрос на файлы или отдельные записи в этих файлах, затем файловый сервер передает запрос к клиенту. Файловый сервер поддерживает целостность данных и безопасность доступа к файлу • Сервер баз данных Разбивает работу приложения между различными компьютерами в сетевой среде. Клиент формирует запросы на получение некоторого элемента данных, затем сервер баз данных находит эти данные и передает запрос клиенту. Сервер баз данных может обрабатывать сложные информационные запросы, для удовлетворения которых могут понадобиться мошности нескольких баз данных • Серверы транзакций Используется для выполнения транзакций, которые происходят на компьютере или компьютерах, содержащих сервер транзакций. Каждое действие или обновление выполняется полностью без прерывания. При возникновении некоторых проблем все действия или обновления отменяются, и делается новал попытка выполнить транзакцию • Логические серверы Используется для решения задач, которые требуют интенсивных символьных вычислений. Логический сервер способен отыскать как неявно, так и явно заданную информацию в базе данных. Логический сервер способен проследить некоторую информацию и сделать логический вывод об информации, которая не была явно введена в базу данных. Логический сервер состоит из базы данных с одним или несколькими встроенными механизмами логического вывода. Этот механизм используется для получения заключений и выводов от сервера. База данных состоит из правил, теорем, аксиом и процедур. Чтобы удовлетворить запросы, логический сервер должен применять дедукцию, индукцию, силлогизмы и другие приемы «Классная доска» и мультиагентные системы — это две основные архитектуры, используемые в данной книге для поддержки параллельного и распределенного программирования. Особое внимание мы уделяем логическим серверам (см. табл. 8.6). Логический сервер — это специальный тип сервера приложений, который используется для решения задач, требующих интенсивных символьных и, возможно, параллельных вычислений. Процесс формирования некоторого вывода и делукции часто тяжелым бременем ложится на процессор и может значительно выиграть от использования параллельно работающих процессоров. Обычно чем больше процессоров доступно логическим серверам, тем лучше. Мультиагентные архитектуры и архитектуры «классной доски», рассматриваемые в главах 12 и 13, опираются на понятие распределенных логических серверов, которые могут совместными усилиями решать проблемы в сетевой среде, intranet или Internet. Несмотря на то что агентный подход и стратегия «классной доски» формируют архитектуру с уклоном в сторону равноправных узлов, они являются клиентами логических серверов. Распределенные объекты используются для реализации всех компонентов системы, а CORBA-спецификация позволяет упростить сетевое программирование. Резюме Распре д еленное программирование включает программы, которые выполняются в различных процессах. Все процессы потенциально разме щ аются на различных компьютерах и, воз м ожно, в различных сетях с различны м и сетевы м и протокола м и. Мето д ы распре д еленного програ мм ирования позволяют разработчику раз д елить приложение на от д ельно выполняе м ые модули , отношения между которы м и м ожно определить на основе равноправия или как «изготовитель-потребитель». Каж д ый м о-луль имеет собственное а д ресное пространство и ко м пьютерные ресурсы. Распределенное програ мм ирование позволяет использовать преи м у щ ества специальных процессоров, периферийного оборудования и других ко м пьютерных ресурсов (например, серверов баз данных, приложений, почтовых серверов и т.д.). CORBA — это стандарт, применяемый для распре д еленного объектно-ориентированного программирования. В этой главе расс м атриваются только CORBA — спецификации и CORBA-службы. Здесь вы должны были получить представление об этих базовых компонентах ио том, как можно построить простую распределенную программу. CORBA-спецификации для Web-служб, MAF, службы имен можно получить по адресу: www.omg.org . За подробностями можно обратиться к книге [20]. Именные и маклерские графы обеспечивают основу для мо щ ного распределенного механизма представления знаний, который можно использовать в сочетании с мультиагентным програ м мированием. Они создают основу для следую щ его уровня интеллектуальных Web-служб. Примечания:1 POSIX— Portable Operating System Interface for computer environments— интерфейс переносимой операционной системы (набор стандартов IEEE, описывающих интерфейсы ОС для UNIX). 13 Мы не включаем многопоточные программы в категорию распределенных 14 Все примеры использования CORBA-компонентов в этой книге реализованы с использованием версии MICO 2.3.3 в операционной системе SuSE Linux и версии MICO 2.3.7 в операционной системе Solaris 8. 15 Вызовы удаленных объектов вносят задержку во времени, необходимость выполнять требования безопасности и возможность возникновения частичных отказов. 16 wCorba - это стандарт CORBA для беспроводного взаимодействия удаленных объектов. Материалы по стандарту wCORBA доступны по адресу: www.omg.org. |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||