Вопросы создания CORBA-приложений

Александр Цимбал

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

Область применения CORBA

CORBA создавалась как универсальная инфраструктура сложных и надежных распределенных систем. Под "сложными" понимаются системы с сотнями и тысячами серверов и миллионами клиентов, работающими в гетерогенных средах. Требования к надежности CORBA-систем подразумевает обеспечение уровня надежности проектов в области телекоммуникаций, финансов или здравоохранения. Именно такой уровень имелся в виду при разработке спецификаций CORBA. Естественно, это привело к появлению того, что c некоторой натяжкой можно назвать "философией" CORBA. Очень кратко ее можно охарактеризовать так: стремление к формализации как проекта в целом, так и его составных частей на как можно более высоком уровне абстракции. Это означает, что б о льшая часть работы, требующей интеллектуальных усилий, должна быть выполнена не на этапе кодирования с использованием того или иного конкретного языка программирования, а на этапе создания спецификации проекта на специальном языке описания его составных частей (Interface Definition Language, IDL). Помимо использования IDL, предполагается активное применение так называемых design patterns - шаблонов тех или иных программных конструкций. Пожалуй, наиболее распространенным таким шаблоном является "фабрики" (factory). Под фабриками понимаются конструкции, предназначенные для (удаленного) создания фрагментов системы, т.е. объектов.

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

Ниже мы попытаемся в самом общем виде описать наиболее важные особенности создания CORBA-приложений.

CORBA или более специализированный подход?

В настоящий момент самым серьезным недостатком CORBA является отсутствие собственной компонентной модели (появление утвержденных OMG спецификаций такой компонентной модели ожидается к концу 2000 г.). Это означает, что CORBA берет на себя проблему создания и поддержки ОБЪЕКТОВ CORBA, но не КОМПОНЕНТОВ CORBA. Главным отличием компонента от объекта (объекта в смысле объектно-ориентированного программирования, а не в терминах технологии CORBA) является включенность компонента в некоторую стандартную среду со строго формализованными правилами поведения, в то время как объект склонен к определенной "анархии".

Вследствие этого, перед разработчиком (точнее, перед создателем проекта) в настоящее время стоит очень серьезная проблема выбора между двумя подходами: либо использовать "чистую" технологию CORBA, либо отказаться от многих ее возможностей, но приобреcти взамен готовые технологические решения, предлагаемые некоторой сторонней технологией, тоже основанной на CORBA (или активно использующей ее), но не совпадающей с ней. Читатель, который имеет представление о состоянии дел в современной компьютерной индустрии, поймет, что речь идет, скорее всего, о технологии Enterprise JavaBeans (EJB).

EJB предоставляет готовую компонентную модель для решения таких проблем, как эффективное управление ресурсами серверов (в первую очередь, оперативной памятью), сетевыми ресурсами, а также для универсального управления транзакциями. В качестве "платы" за предоставляемые удобства от проектировщика требуется подчиниться достаточно жестким требованиям этой компонентной модели, а именно: использование на стороне сервера только языка Java; ориентация на два фиксированных типа серверных компонентов, один из которых (так называемый "session bean") предназначен для формализации отношений между конкретным клиентом и сервером EJB, а второй ("entity bean") является объектным представлением данных в БД; следование жестким правилам создания EJB-приложений.

Технология EJB имеет свою философию, в общем, отличную от философии CORBA. EJB, бесспорно, предоставляет большие преимущества по сравнению с классической CORBA в случае создания систем, основанных на использовании той или иной базы данных в качестве ядра проекта (хорошим примером может служить бухгалтерская система или система автоматизации торговли). В то же время, подход в стиле EJB вряд ли может рассматриваться как по-настоящему универсальный в рамках создания распределенных систем вообще. Наверное, между EJB-проектами и проектами в классическом стиле CORBA существует такая же разница, как между приложениями, ориентированными на обслуживание БД, и произвольными приложениями для решения самых различных задач.

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

Использование IDL

Создание любого CORBA-проекта начинается с разработки спецификаций этого проекта на языке IDL. IDL является "сердцем" технологии CORBA. Он позволяет определить абсолютно все аспекты поведения объектов, из которых строится ваша система, которые важны с точки зрения взаимодействия этих объектов между собой. Важно понимать, что никакие детали реализации этих объектов на этапе создания IDL-деклараций во внимание не принимаются. В частности, IDL-декларации совершенно не позволяют делать никаких предположений о наличии или отсутствии состояний ваших объектов - это их "внутреннее дело".

Дизайнеру проекта крайне важно научиться "мыслить" на языке IDL. Он играет для CORBA-проекта роль, во многом схожую с ролью CASE-диаграмм, а именно, позволяет создать достаточно наглядное описание функциональности отдельных составных частей и задать связи между ними на абстрактном уровне. При этом набор IDL-объявлений является скелетом вашей распределенной системы, а не просто описанием, которое нуждается в более детальной проработке на этапе реализации. Большинство изменений, которые вам потребуется внести в систему и которые влияют на характер взаимодействия любого объекта с другими объектами или "внешним миром", должны быть отражены на уровне его IDL-описаний.

IDL является достаточно выразительным языком. Он позволяет определить типы данных, которые необходимы для функционирования вашей системы, включая структуры, объединения, массивы и так называемые "последовательности" (sequences), который можно рассматривать как массивы переменной длины. Главной задачей IDL является определение интерфейсов удаленных объектов, т.е. совокупности удаленных методов, к которым могут обращаться клиенты в процессе работы распределенной системы. IDL позволяет задать списки аргументов, тип возвращаемого результата, а также исключительные ситуации, которые могут быть возбуждены при вызове этого метода.

Вот фрагмент IDL-файла, который является типичным для CORBA-приложения:

	module MyModule {
		typedef struct Str {
			long	key;
			string	description;
			double	value;
		} MyData;

		typedef sequence<MYDATA> MyDataList;

		exception MyException {};

		interface MyInerface {
			MyDataList MyMethod (in long arg) 
				raises (MyException);
                         };
              };

Модуль MyModule позволяет определить новое пространство имен для того, чтобы избежать случайного совпадение имен интерфейсов, типов и т.д., созданных различными разработчиками в рамках одной распределенной системы. Следующие два объявления определяют типы данных и их имена, которые будут использоваться при взаимодействии с удаленным объектом, поведение которого (с точки зрения удаленного взаимодействия) задано интерфейсом с именем MyInterface. Этот интерфейс определяет, что клиент может вызвать единственный метод данного объекта, который в качестве входного аргумента требует наличия значения целого типа, а возвращает 0, 1 или несколько структур, каждая из которых содержит три поля. При этом может быть возбуждена исключительная ситуация MyException.

Что получает разработчик, создав такие IDL-объявления?

  • он имеет наглядное отображение функциональности создаваемой системы;
  • он имеет все необходимое для автоматической генерации на любом из языков программирования, поддерживающих CORBA, готовых фрагментов, которые обеспечат выполнение удаленного вызова клиентом серверного объекта и возврат результата;
  • он может быть уверен, что корректный вызов метода будет выполнен даже в том случае, если клиентское приложение написано, например, на Java и выполняется на компьютере с процессором Intel под управлением Windows NT, а серверное приложение - на C++ и работает на SPARC-сервере под управлением Solaris;
  • с помощью незначительных изменений (а именно, с помощью наследования интерфейсов) можно сделать метод MyMethod транзакционным, т.е. участвующим в глобальной транзакции. Вот все необходимые изменения:
    interface MyInterface : CosTransaction::TransactionalObject { 
  • при использовании Сервиса Безопасности CORBA администратор системы может предоставить право вызова метода только определенным пользователям.
Таким образом, с помощью создания IDL-объявлений вы попадаете в мир распределенных, транзакционных, защищенных программ, не зависящих ни от платформ, ни от операционных систем, ни от языков программирования, ни от аппаратных особенностей используемых архитектур. Единственное, что остается сделать - это запустить компилятор idl2xxx (idl2cpp, idl2java, ...), который сгенерирует как основу вашего серверного приложения, так и код взаимодействия вашего клиентского приложения с инфраструктурой CORBA.

Понятие об объектах CORBA и объектных адаптерах

Термин "объект" занимает центральное место в современных информационных технологиях. Понимание сути этого термина применительно к той или иной конкретной технологии является чрезвычайно важным для архитектора проекта (для "обычного" программиста это справедливо в гораздо меньшей степени). Поскольку объект CORBA имеет много особенностей по сравнению с привычными объектами ООП, мы остановимся на этом подробнее.

Первое и самое главное положение: объект CORBA не является переменной языка программирования - это концептуальное понятие самой технологии, а не ее отображения на конкретный язык программирования, использованного для написания конкретной программы. Объекты CORBA не занимают места в оперативной памяти, они не "захватывают" сетевые ресурсы и даже не обязательно сохраняются в долговременной памяти - файлах или базах данных.

Второе (и не менее главное): объекты CORBA реально существуют - предусмотрены команды явного или неявного их создания; результатом их создания является так называемая "объектная ссылка" CORBA, которая представляет собой совокупность вполне конкретной и стандартизованной информации; можно говорить о цикле жизни таких объектов.

Такое сочетание свойств является не вполне обычным, но это действительно так. Архитектор проекта должен оперировать объектами, сосредоточив все свое внимание на функциональности системы, а не на том, сколько для реализации такой системы потребуется реальных ресурсов. Например, нетрудно представить себе программный комплекс, который сопоставляет отдельный объект CORBA с каждой из десятков миллионов записей в реляционной базе данных. Эти миллионы объектов CORBA должны быть созданы и будут существовать, но при этом не занимать никаких дополнительных ресурсов на сервере.

Третье положение: с объектом CORBA нельзя "общаться" до тех пор, пока для него не создана конкретная переменная того или иного языка программирования, предназначенная именно для взаимодействия между клиентом и данным CORBA-объектом. Для объектно-ориентированных языков программирования - С++, Java, Smalltalk, Delphi - эта переменная и будет классическим объектом в смысле ООП. На языке CORBA такой объект называется "сервантом" (servant). Ресурсы серверов занимают серванты, а не CORBA-объекты. CORBA предоставляет огромные возможности по организации взаимодействия между CORBA-объектами и их сервантами. Это один из столпов, на которых базируется масштабируемость CORBA-приложений.

Важной особенностью CORBA-объектов является то, что хотя существуют несколько команд их создания, не существует явной команды уничтожения (если не считать метода remove Сервиса Цикла Жизни, который, впрочем, должен написать сам разработчик). Уничтожение CORBA-объекта - это последовательность действий, которые приводят к тому, что клиент, имея объектную ссылку как результат выполнения операции создания CORBA-объекта, не может получить по ней доступ к этому объекту. Эта последовательность действий (или их отсутствие) может быть различна для разного вида объектов. Например, для уничтожения так называемых "временных" (transient) CORBA-объектов ничего делать специально не надо - их уничтожение происходит как бы "само собой". В других случаях от программиста может потребоваться, например, явное удаление состояния уничтожаемого объекта из базы данных.

На уровне IDL-деклараций основу для создания CORBA-объектов предоставляют интерфейсы. Соотношение между интерфейсом IDL и объектом очень похоже на соотношение между типом и переменной такого типа в любом языке программирования.

Обеспечение "стыковки" между миром CORBA и конкретной программой на конкретном языке программирования (т.е., говоря несколько упрощенно, между CORBA-объектом и его сервантом) занимается компонент CORBA, который называется "объектным адаптером". Без глубокого знания возможностей объектных адаптеров невозможно написать высококачественное CORBA-приложение.

Хотя объектные адаптеры явно и не присутствуют в IDL-объявлениях, созданных разработчиками конкретных проектов, в правильно разработанных системах они присутствуют косвенно - с ними очень тесно связаны так называемые "фабрики объектов".

Фабрики объектов и объектные адаптеры

В любой грамотно спроектированной CORBA-системе должны явно, в виде IDL, присутствовать фабрики объектов, т.е. интерфейсы, содержащие методы, задачей которых является создание удаленных CORBA-объектов и/или их сервантов. Возникает естественный вопрос: зачем разработчику создавать такие интерфейсы, если именно для этой цели и существуют стандартные объектные адаптеры?

Причина здесь следующая: процесс создание и управления CORBA-объектами и их сервантами имеет много особенностей и ограничений, и крайне желательно, чтобы учет этих особенностей получил отражение на уровне IDL-объявлений. Вряд ли можно считать разумной практику, при которой создание фрагментов системы (а объекты являются именно фрагментами распределенных систем) не отражено в спецификации проекта, а оставлено на рассмотрение программиста.

Какие же особенности создания сервантов нужно учесть при разработке спецификации?

В настоящий момент основным объектным адаптером, используемым в технологии CORBA, является так называемый Portable Object Adapter (POA). Ниже приведены его основные особенности, которые необходимо учесть на стадии разработки проекта.

  • POA способен создавать объекты только в адресном пространстве того приложения, в котором он сам существует.
  • POA способен создавать объекты только с теми характеристиками, которые были заданы на стадии создания самого POA. Например, один и тот же POA не может создавать одновременно и "временные" (transient), и "долгоживущие" (persistent) объекты - необходимо для этого сначала создать два различных POA.
  • Несколько POA, используемых в сервере приложений, существуют не отдельно друг от друга, а образуют иерархию POA, которая топологически представляет из себя дерево. Хотя POA в такой структуре и не наследуют свойств друг друга, правильное ее создание жизненно важно для проекта, так как уничтожение структуры POA (например, при завершении работы приложения) выполняется в определенном порядке - сначала "дочерние" адаптеры, затем их "родители". Поскольку POA отвечают не только за создание объектов и их сервантов, но и за доставку запроса клиента нужному серванту, очень важно, чтобы POA, "отвечающие" за объекты (и их серванты), которые должны быть уничтожены в последнюю очередь, находились как можно ближе к "корню" дерева адаптеров.
Таким образом, разработка интерфейсов-фабрик является очень важной и достаточно сложной задачей, для решения которого нужно учитывать большое количество конкретных особенностей того или иного проекта, так как эти фабрики самым тесным образом (на уровне реализации методов этих интерфейсов) связаны с реальными объектными адаптерами CORBA. Простейший пример:
            interface GeneralFactory { 	
                     MyInterface_1 create_object_1 ();
                     MyInterface_2 create_object_2 ();
            };

Такая IDL-декларация подразумевает, что CORBA-объекты, которые с точки зрения клиента существуют как объектные ссылки на MyInterface_1 и MyInterface_2, функционально тесно связаны друг с другом. Кроме того, можно предположить, что для каждого сервера приложений, в котором могут быть созданы такие объекты, должны быть созданы (в общем случае) не менее трех POA: стандартный "корневой" POA и два "производных" от него адаптера, каждый из которых предназначен для создания своего вида объектов.

Если же объекты MyInterface_1 и MyInterface_2 слабо связаны друг с другом (или вообще не связаны), то целесообразно создать два отдельных интерфейса-фабрики, например, так:

 
              interface Factory_1 {
                      MyInterface_1 create_object ();
              };

              interface Factory_2 {
                      MyInterface_2 create_object ();
               };

Наследование интерфейсов

IDL позволяет использовать наследование интерфейсов:

             interface BaseInterface { ... };
             interface DerivedInterface : BaseInterface { ... };
Возникает вопрос, как следует в свете таких объявлений
рассматривать следуюший интерфейс:
                  interface MyFactory {
                        BaseInterface create_object ();
                  };

Архитекторам проектов, привыкшим к использованию объектно-ориентированных языков, нужно быть здесь очень осторожными. Язык IDL не является объектно-ориентированным, так как это привело бы к слишком большим трудностям при отображении с него на C, Ada, Cobol и другие языки, непосредственно не поддерживающие ООП. Нужно понимать, что наследование интерфейсов НЕ ОЗНАЧАЕТ наследование их реализаций. При использовании C++ или Java разработчик может выбрать сам, является ли класс, реализующий интерфейс DerivedInterface, производным от класса, реализующего интерфейс BaseInterface, или это совершенно независимые друг от друга классы. Вследствие всего этого, метод create_object() правильнее рассматривать как средство для создания объектов именно типа BaseInterface, а не любого из типов BaseInterface и DerivedInterface, хотя легко, например, для C++, создать и такую реализацию.

Обеспечение доступа к объектам

Вопросы, затронутые в трех предыдущих разделах, касались создания серверных CORBA-объектов. Очевидно, что серверные CORBA-объекты должны быть тем или иным образом сделаны доступными для клиентов. CORBA предоставляет несколько способов организации доступа клиентов к серверным объектам. Основными из них являются Сервис Именования (Naming Service) и Трейдер-Сервис (Trader Service). Часто фирмы, занимающиеся реализацией CORBA, предоставляют дополнительно свои собственные средства поиска. Примером такого нестандартного, но очень удобного средства является Location Service фирмы Inprise/Borland для VisiBroker.

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

  • непосредственное опубликование информации о каждом таком объекте;
  • опубликование информации о диспетчере объектов, который позволяет получить доступ к группе объектов с использованием одного или нескольких методов поиска;
  • передача клиенту объектной ссылки на объект в качестве вспомогательного выходного аргумента или результата вызова некоторого удаленного метода (этот способ можно рассматривать как частный случай предыдущего).

Четко разделить объекты на "доступные клиентам" и "внутренние" очень важно как с точки зрения эффективности создаваемого приложения (недоступные клиенту объекты в общем случае занимают меньше ресурсов), так и с точки зрения сложности создаваемых программ - использование и Naming Service, и Trader Service приводит к необходимости написания дополнительного кода.

Выводы

В самом общем виде процесс создания универсального CORBA-приложения должен содержать следующие шаги:

  • создание IDL-деклараций, описывающих поведение всех объектов распределенной системы. При этом следует принять во внимание особенности создания объектов, наличие транзакций, вопросы обеспечения устойчивости к сбоям и многое другое;
  • автоматическая генерация кода как на стороне клиента, так и на стороне сервера кода, обеспечивающего взаимодействие ваших программ с коммуникационной системой CORBA, на выбранном языке программирования;
  • реализация методов интерфейсов на стороне сервера на том или ином языке программирования. На этой стадии может потребоваться принятие решений о наследовании классов реализаций;
  • создание серверных CORBA-объектов (не обязательно вместе с их сервантами);
  • обеспечение доступа для клиентов ко всем или некоторым серверным объектам;
  • запуск тех или иных необходимых сервисов - Naming Service, Trader Service,
  • Event Service, Object Transaction Service, Location Service и др.;
  • запуск серверных приложений или их регистрация в так называемых Репозитариях Реализаций, что позволяет обеспечить удаленный запуск серверов приложений по запросам клиента;
  • запуск клиентских приложений, получение объектных ссылок на серверные объекты и выполнение удаленных вызовов.
Стиль создания клиентских CORBA-приложений очень мало отличается от стиля написания "обычных" программ - все вопросы удаленного взаимодействия, управления транзакциями, обеспечения безопасности и пр. решаются CORBA практически прозрачным для разработчика способом.

И, наконец, последнее замечание. Не следует думать, что CORBA имеет смысл применять только для огромных и сложных систем - эта технология обеспечивает реальную масштабируемость создаваемых приложений. Ориентация главным образом на сложные проекты имеет не технологический, а чисто экономический аспект - за стандартные компоненты CORBA нужно платить, и эти затраты могут просто не окупиться при создании простых и недорогих систем. Это единственное, что не позволяет автору настоящего обзора рекомендовать использовать CORBA как рабочий инструмент для создания проектов любой сложности.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=4224