Переносимость и интероперабельность информационных систем и международные стандарты

Сергей Кузнецов


Программные продукты, которые сегодня принято называть информационными системами, появились много лет назад. Первые информационные системы основывались на мейнфреймах компании IBM, файловой системе ОС/360, а впоследствии на ранних СУБД типа IMS и IDMS. Эти системы прожили долгую и полезную жизнь, многие из них до сих пор эксплуатируются. Но с другой стороны, полная ориентация на аппаратные средства и программное обеспечение IBM породила серьезную проблему "унаследованных систем" (legacy systems). Проблема состоит в том, что производственный процесс не позволяет прекратить или даже приостановить использование морально устаревших систем, чтобы перевести их на новую технологию. Многие серьезные исследователи сегодня заняты попытками решить эту проблему. Это отдельная большая тема, но в этом докладе мы серьезно рассматривать ее не будем.
Серьезность проблемы унаследованных систем очевидно показывает, что информационные системы и лежащие в их основе базы данных являются слишком ответственными и дорогими продуктами, чтобы можно было позволить себе их переделку при смене аппаратной платформы или даже системного программного обеспечения (главным образом, операционной системы и СУБД). Для этого программный продукт должен обладать свойствами легкой переносимости с одной аппаратно-программной платформы на другую. (Это не означает, что при переносе не могут потребоваться какие-нибудь изменения в исходных текстах; главное, чтобы такие изменения не означали переделки системы.)
Другим естественным требованием к современным информационным системам является способность наращивания их возможностей за счет использования дополнительно разработанных, а еще лучше, уже существующих программных компонентов. Для этого требуется обеспечение свойства, называемого интероперабельностью. Под этим понимается соблюдение определенных правил или привлечение дополнительных программных средств, обеспечивающих возможность взаимодействия независимо разработанных программных модулей, подсистем или даже функционально завершенных программных систем.
Каким же образом можно одновременно удовлетворить оба эти требования уже на стадии проектирования и разработки информационной системы? Ответом, который мне нравится и кажется правильным, является следующий: следуйте принципам Открытых Систем. Другими словами, максимально строго придерживайтесь международных или общепризнанных фактических стандартов во всех необходимых областях.
Рассмотрим немного подробнее, какие стандарты следует иметь в виду при разработке информационной системы сегодня. При использовании текущей технологии информационная система пишется на некотором языке программирования, в нее встраиваются операторы языка SQL и, наконец, включает какие-либо вызовы библиотечных функций операционной системы.
Соответственно, прежде всего следует обращать внимание на степень стандартизированности используемого языка программирования. На сегодняшний день приняты международные стандарты языков Фортран, Паскаль, Ада, Си и, совсем недавно, Си++. Понятно, что Фортран, даже в своем наиболее развитом виде стандарта Фортран-95, не является языком, подходящим для программирования информационных систем. Паскаль - очень приятный язык, но чтобы не испортить впечатление от его приятности, в стандарт не включены средства раздельной компиляции. Конечно, в принципе можно оформить полный исходный текст в виде одного текстового файла, но вряд ли это разумно и практично. Язык Ада, вообще говоря, пригоден для любых целей. На нем можно писать и информационные системы (что, кстати и делают американские и некоторые отечественные военные). Но что-то не видно счастливых прикладных программистов, использующих язык Ада. Уж больно он громоздкий...
По мнению многих программистов, наиболее хороший стандарт на сегодняшний день существует для языка Си. Опыт нескольких лет, прошедших после принятия стандарта, показывает, что при грамотном использовании стандарта Си ANSI/ISO проблемы переноса программ, связанные с особенностями аппаратуры или компиляторов, практически не возникают (если учитывать имеющиеся в самом стандарте рекомендации по созданию переносимых программ). Этих нескольких лет оказалось достаточно, чтобы обеспечить полное соответствие стандарту практически всех индустриальных компиляторов языка Си. Даже если в реализации допускаются расширения стандарта (как, например, в компиляторе Ричарда Столлмана из проекта GNU GCC), то имеются опции компилятора, позволяющие контролировать выход за границы стандарта.
Очень важно, что в стандарте ANSI Си специфицирован не только язык, но и базовые библиотеки стандартной системы программирования (в частности, основные компоненты библиотеки ввода/вывода). Наличие стандарта на библиотечные функции и его строгое соблюдение в реализациях в ряде случаев позволяет создавать не очень сложные приложения, переносимые не только между разными аппаратными платформами, но и между различными операционными средами.
Прошлым летом был, наконец, принят стандарт Си++. Видимо, это означает (по крайней мере, на это можно надеяться), что еще через несколько лет можно будет говорить о мобильном программировании на Си++ в том же смысле, в котором можно говорить об этом сегодня по отношению к Си. С другой стороны, трудно надеяться, что действительно стандартные компиляторы появятся очень быстро: слишком сильно сегодня расходятся реализационные варианты языка Си++.
Почему, имея в виду взаимодействия с базами данных, мы говорим про язык SQL, и что с ним происходит? Здесь все не очень просто. SQL с самого своего зарождения являлся сложным языком со смешанной декларативно-процедурной семантикой, не идеальным синтаксисом, а кроме того, всегда содержал ряд темных мест (объем доклада не позволяет даже привести примеры). Тем не менее, судьба распорядилась так, что именно SQL стал единственным практически используемым языком реляционных баз данных, хотя имелись и другие кандидаты, в частности, язык системы Ingres Quel.
В результате длительного процесса стандартизации языка к настоящему времени имеется два принятых международных стандарта SQL - SQL/89 и SQL/92. Стандарт SQL/89 очень слабый, многие важные аспекты в нем не определены или оставлены для определения в реализации. С другой стороны, большинство современных коммерческих реляционных СУБД на самом деле соответствует стандарту SQL/89 (этого не очень сложно добиться по причине отмеченной выше слабости стандарта).
Стандарт SQL/92 является существенно более продвинутым. В нем точно специфицированы такие важные аспекты, как средства манипулирования схемой базы данных, динамический SQL, расширены возможности языка для формулировки запросов в алгебраическом стиле и т.д. Однако язык SQL/92 настолько сложен, что к настоящему времени нет практически ни одной СУБД, в которой он был бы полностью реализован (как правило, реализуется расширенное подмножество языка).
Ситуация не из приятных. Но тем не менее, внимательный анализ языка показывает, что имеется практическая возможность создания достаточно переносимых программ с использованием SQL/89. Для это нужно максимально локализовать те части программы, которые содержат нестандартизованные конструкции, и стараться не использовать расширения языка, предлагаемые в конкретной реализации. Понятно, что для этого требуется хорошее знание как SQL/89, так и особенностей диалекта, реализованного в используемом продукте. К сожалению, в отличие от ситуации со стандартом языка Си, стандарт SQL/89 не слишком помогает в решении этой проблемы. В этом стандарте полностью отсутствуют какие-либо рекомендации для мобильного программирования информационных систем.
Между прочим, аналогичная ситуация существует и в области операционных систем. Существующий сегодня набор стандартов происходит от интерфейсов операционной системы UNIX (SVID, POSIX, XPG и т.д.). В большинстве современных операционных систем эти стандарты поддерживаются, но, как правило, любая ОС содержит множество дополнительных средств. При этом, естественно, наименее стандартизованы наиболее современные и продвинутые решения (их просто не успевают стандартизовать). Наверное, сегодня самым ярким примером печального положения дел является полная нестандартизованность средств "легковесных процессов" (threads). Нельзя сказать, что процессы, работающие на общей виртуальной памяти, действительно обеспечивают возможность параллельного программирования в среде симметричных мультипроцессорных систем. Это затруднительный в отладке и чреватый скрытыми (редко проявляющимися) ошибками способ программирования. Однако на сегодняшний день наука программирования все еще не может предложить более надежный и простой при использовании подход. Конечно, очень жаль, что отсутствует стандартизация threads.
Если стремиться к достижению переносимости приложения, то следует стараться ограничить себя минимально достаточным набором стандартных средств. В случае, когда нестандартное решение некоторой операционной системы позволяет существенно оптимизировать работу информационной системы, разумно предельно локализовать те места программы, в которых это решение используется.
То, о чем мы говорили до сих пор, относится большей частью к сравнительно небольшим и сравнительно централизованным системам (конечно, даже сравнительно централизованные системы сегодня, как правило, основываются на архитектуре "клиент-сервер"). Однако, если иметь в виду более масштабные проекты, реализуемые не в локальных, а как минимум корпоративных сетях, то проблема обеспечения интероперабельности отдельно спроектированных и разработанных программных компонентов существенно усложняется. При создании крупных распределенных программных систем практически невозможно обязать многочисленных разработчиков следовать общей технологии (может быть и потому, что общепринятая технология сегодня отсутствует).
У разных людей разные вкусы и пристрастия. Одни предпочитают работать в среде открытых систем (например, с использованием языков Си или Си++ в UNIX-совместимом операционном окружении). Других больше устраивает замкнутая среда Smalltalk. В конечном же счете система должна быть интегрирована и по мере возможности обладать достаточной эффективностью.
Проследим наиболее важные вехи в процессе обеспечения интероперабельности распределенных программных компонентов. Возможно, это частная точка зрения, но кажется, что первым шагом был механизм "программных гнезд" (sockets), разработанный в университете Беркли. Основным достижением этой разработки было то, что с использованием единого механизма межпроцессных взаимодействий можно было интегрировать программные компоненты, произвольным образом распределенные в локальных или территориально распределенных сетях. (Конечно, мы должны заметить, что в каждом узле сети должен был поддерживаться механизм программных гнезд.) Основным недостатком программных гнезд является то, что это чисто транспортный механизм. В частности, представление передаваемых по сети данных является машинно-зависимым. Следовательно, в каждом узле сети (если она гетерогенна, а это общий случай) нужно уметь правильно преобразовывать представление данных, получаемых из любого другого узла. Понятно, что это образует серьезную техническую проблему.
Следующим шагом (очень важным шагом) явилось появление протокола вызова удаленных процедур (Remote Procedure Calls - RPC) и его реализация. Идея этого механизма очень проста. В большинстве случаев взаимодействие программных компонентов является асимметричным. Практически всегда можно выделить клиента, которому требуется некоторая услуга, и сервер, который такую услугу способен оказать. Более того, как правило, клиент не может продолжать свое выполнение, пока сервер не произведет требуемые от него действия. Другими словами, типичным взаимодействием клиента и сервера является процедурное взаимодействие. С точки зрения клиентской программы обращение к серверу ничем не отличается от вызова локальной процедуры. При реализации механизма вызова удаленных процедур на основании спецификации интерфейса процедуры на стороне клиента генерируется локальный представитель удаленной процедуры - stub, а на стороне сервера - специализированный переходник - proxy.
Очень полезной составляющей (относительно независимой от других составляющих) семейства протоколов RPC является протокол внешнего представления данных (External Data Representation - XDR). В этом протоколе специфицировано машинно-независимое представление данных, понимаемых механизмом RPC. Идея состоит в том, что при передаче параметров вызова удаленной процедуры и при получении ее ответных параметров происходит двойное преобразование данных сначала из исходного машинно-зависимого формата в формат XDR, а затем из этого формата в машинно-зависимый целевой формат. В результате взаимодействующие программы избавлены от потребности знания специфики машинно-зависимых представлений данных. Возможно, именно протокол XDR обладает определяющим значением в связке протоколов вызова удаленных процедур (хотя, конечно, возможности XDR довольно ограничены).
Наконец, видимо наиболее перспективным на сегодняшний день является подход, предложенный и специфицированный международным консорциумом Object Management Group (OMG). Опять- таки, основная идея проста. Как бы мы не стремились выработать единые языковые средства для разработки распределенных информационных систем, похоже, что это стремление останется среди других благородных, но недостижимых задач человечества. Унификация невозможна. Имеется много разных подходов, каждый из которых в чем-то превосходит другие. Нельзя сказать, что какой-то подход является определяющим. Пожалуй, единственное, на чем сходится подавляющее большинство исследователей и разработчиков распределенных программных систем, это склонность к использованию парадигмы объектной ориентации.
Не будем подробно останавливаться на преимуществах этой парадигмы (об этом давно написаны целые книги). Однако все-таки заметим, что объектно-ориентированный стиль проектирования и разработки программных систем (и в особенности информационных систем) стимулирует повторное использование программных компонентов (за счет наличия механизма наследования классов), обеспечивает независимость использования информационных ресурсов от особенностей их реализации (за счет применения принципов инкапсуляции), наконец, при проектировании и разработке информационных систем позволяет производить комплексную разработку структур данных и управляющих ими процедур (с применением технологии объектно-ориентированных систем баз данных).
Все это прекрасно. Но проблема состоит в том, что отсутствует общая объектная модель. В разных объектно-ориентированных системах программирования и системах баз данных гораздо больше общего, чем различного, но их различия часто принципиальны настолько, что объекты, разработанные и созданные в разных системах, не способны взаимодействовать. Они не понимают друг друга, они не интероперабельны. А это, конечно, очень плохо. Особенно плохо по той причине, что постоянное наращивание возможностей Internet делает принципиально возможным использование информационных ресурсов вне зависимости от их реального расположения.
Активность OMG предлагает ограниченное, но практически достижимое решение этой проблемы. Это общая архитектура Object Management Architecture (OMA) и ее конкретное воплощение Common Object Request Broker Architecture (CORBA). Мы остановимся только на наиболее ключевых аспектах. Прежде всего, поскольку на сегодня отсутствует (и вряд ли появится в ближайшем будущем) объектная модель, которая могла бы служить "общей крышей" для существующих объектно-ориентированных языков и систем программирования, то единственным практически возможным выходом была выработка минимальной объектной модели, обладающей ограниченными возможностями, но имеющая явные аналоги в наиболее распространенных объектных системах. В архитектуре CORBA такая минимальная модель называется Core Object Model, и ей соответствует язык спецификации (не программирования!) интерфейсов объектов Interface Definition Language (IDL).
Чтобы обеспечить возможность взаимодействия объекта, существующего в одной системе программирования, с некоторым объектом из другой системы программирования, в исходный текст первого объекта (правильнее сказать, его класса) должна быть помещена спецификация на языке IDL интерфейса того объекта, метод которого должен быть вызван. Аналогичная спецификация должна быть помещена на стороне вызываемого объекта. Далее исходные тексты должны быть обработаны соответствующим прекомпилятором IDL (таких прекомпиляторов должно иметься ровно столько, сколько поддерживается интероперабельных объектных сред; сегодня специфицированы правила встраивания IDL в программы на языках Си, Си++ и Smalltalk). Спецификация интерфейса объекта на языке IDL представляет собой главным образом набор сигнатур методов объекта. В каждой сигнатуре определяется имя метода, список имен и типов данных входных параметров, а также список имен и типов данных результатов выполнения метода. Кроме того, IDL позволяет определять структурные типы данных (конечно, прекомпилятор IDL на основе определений таких типов производит спецификации типов целевой системы программирования)
Что же является результатом работы прекомпилятора? Все очень похоже на RPC... На стороне клиента генерируется текст объекта-stub, который играет роль локального представителя удаленного объекта (с ограничениями, свойственными IDL). Работа с этим объектом-посредником происходит по правилам соответствующей системы программирования: в среде Си++ stub - это объект Си++, в среде Smalltalk - это объект Smalltalk и т.д. С другой стороны, в коде, генерируемом в stub, заложены знания о том, что требуется сделать для обращения к методу удаленного объекта.
На стороне сервера генерируется текст объекта-sceleton, своего рода посредника при доступе к удаленному объекту. С одной стороны, sceleton "знает", что обращение является удаленным. С другой стороны такой посредник умеет обращаться к удаленному объекту по правилам его системы программирования.
Конечно, для доставки сообщения от клиента к серверу и получения ответных результатов должен существовать системный (вернее, полусистемный - middle-ware) компонент, отвечающий именно за выполнение таких функций. В архитектуре CORBA такой компонент называется брокером объектных заявок (Object Request Broker - ORB). В функции ORB главным образом входит передача данных в машинно-независимом формате от клиента серверу и от сервера клиенту. Кроме того, ORB отвечает за правильное указание сетевого адреса объекта-сервера.
Интерфейс между stub, sceleton и ORB основан на специфицированном в описании архитектуры CORBA интерфейсе прикладного уровня (Application Programmer Interface - API). Этот интерфейс на самом деле открыт для программистов. На нем основан метод динамического вызова объектов без потребности спецификации их интерфейсов на языке IDL. Естественно, программирование на уровне API ORB является гораздо более обременяющим делом, чем если бы использовался IDL. Однако иногда интерфейсы вызываемых удаленных объектов просто неизвестны в статике; их можно получить только на стадии выполнения программы, и тогда без использования API обойтись не удается.
OMG не обладает правами международной организации по стандартизации. Тем не менее, принимаемые в этой организации документы обладают исключительно высоким международным авторитетом и становятся фактическими стандартами. Проблемой стандарта OMG CORBA было то, что отсутствовала стандартизация взаимодействий уровня ORB-ORB. В результате ORB'ы, производимые разными компаниями, не могли совместно работать. Теперь уже ORB'ы не понимали друг друга. В прошлом году OMG приняла новый стандарт CORBA-2, в котором специфицирован протокол взаимодействий между ORB'ами. Реализации этого протокола уже появились, и имеется надежда на реальное использование информационных ресурсов Internet. Дай- то Бог!
Конечно, CORBA - это далеко не все, что требуется. Это всем понятно, но далеко не понятно, а что же нужно. Это ужасно сложная и непонятная тема - семантическая интероперабельность объектных систем.

[Назад] [Содержание] [Вперед]