Распределенные вычисления и технологии Inprise
1. Зачем нужен Microsoft Transaction Server
4. Пример 1: создание простейшего серверного объекта
5. Отладка серверных объектов MTS
6. Пример 2: создание объектов для управления распределенными транзакциями
Использование Microsoft Transaction Server для управления распределенными транзакциями:
общие вопросы
1. Зачем нужен Microsoft Transaction Server
1.1. COM и распределенные вычисления
В предыдущей статье данного цикла были рассмотрены общие вопросы организации распределенных вычислений и общие принципы взаимодействия клиентов и серверов в распределенных системах. Настоящая статья посвящена одной из многочисленных реализаций технологии распределенных вычислений – технологии Microsoft COM (точнее, ее расширению – COM+).
В отличие от технологий CORBA (Component Object Request Broker Architecture) или DCE (Distributed Computing Environment), изначально появившихся как спецификации и лишь затем – как конкретные реализации в виде продуктов тех или иных производителей, Microsoft COM появилась одновременно и в виде спецификации (то есть правил создания серверов и клиентов, описания соответствующего API, диалекта IDL и др.), и в виде реализации (функции Windows API, утилиты в составе различных SDK, поддержка и широкое использование данной технологии в операционных системах Windows 95/98/NT, вплоть до использования реестра в качестве регистрационной базы данных COM-сервисов и списков пользователей сети при определении прав доступа к сервисам, а также поддержка COM в других программных продуктах Microsoft). Это обусловило широкую популярность COM как технологии, реализующей объектно-ориентированный подход не на уровне кода, а на уровне сервисов и приложений операционной системы, несмотря на ограниченный спектр поддерживаемых этой технологией платформ (пока это различные версии Windows, хотя информация о предстоящих реализациях для других операционных систем уже начинает появляться), а также на то, что, в сущности, в COM как технологии организации распределенных вычислений нет ничего революционного. И вызовы удаленных процедур, и создание stub- и proxy-объектов, и регистрация сервисов в специализированных базах данных, и язык IDL как средство описания интерфейсов сервера и сервисов – все это было придумано задолго до возникновения COM (и задолго до появления самой Windows).
Отметим, однако, что COM, если следовать ее спецификациям, позволяет решить множество проблем программирования для Windows, таких как существование различных версий одних и тех же библиотек (плюс возможность замены новых версий библиотек старыми при установке тех или иных программных продуктов), наличие нескольких реализаций одной и той же спецификации сервиса, присутствие нескольких сервисов в одной библиотеке и др., что также существенно прибавило популярность и данной технологии. Однако подробное обсуждение этих возможностей выходит за рамки данной статьи. Интересующиеся могут более подробно ознакомиться с ними на сайте Microsoft (см., например, Brockschmidt K. What OLE is really about, www.microsoft.com/oledev/olecom/aboutole.html ).
Очень существенно, что использование COM для организации распределенных вычислений является одним из самых недорогих решений. Регистрационная база данных (реестр) – это составная часть операционной системы, и, соответственно, ее не нужно приобретать отдельно; поддержка DCOM (Distributed COM) в виде соответствующих сервисов либо также присутствует в операционной системе (Windows NT), либо доступна бесплатно (Windows 95). Сервисы, занимающиеся поиском одной из нескольких реализаций сервера для данного клиента (directory services), в DCOM как таковом отсутствуют – местоположение реализации сервера фиксируется при настройке DCOM для конкретного клиента (есть, конечно, надстройки над COM, обеспечивающие такой сервис, например Inprise OLEnterprise, но их использование не является обязательным).
Из этого, естественно, не следует, что распределенная информационная система с помощью COM/DCOM может быть создана бесплатно. Если удаленный сервер предоставляет клиентам сервисы доступа к данным, необходимо приобрести лицензии на клиентскую часть серверной СУБД (при этом их число может быть равно не числу серверов, а числу конечных пользователей — все определяется лицензионным соглашением производителя серверной СУБД). Помимо этого есть и другие лицензии, которые необходимо приобрести в подобном случае, например лицензия на многопользовательский доступ к Borland Database Engine, входящая в состав продукта Inprise MIDAS. Однако даже с учетом этих затрат общая стоимость такой информационной системы оказывается существенно ниже, чем при использовании, например, Inprise Entera. Естественно, чрезвычайно высоких требований к надежности систем на основе COM при этом предъявлять не стоит, но во многих случаях такое решение может оказаться вполне удовлетворительным.
Весьма популярным направлением развития информационных систем малых и средних предприятий является в настоящее время создание трехзвенных систем с использованием технологии Inprise MIDAS, базировавшейся до недавнего времени на том, что серверы доступа к данным представляют собой не что иное, как COM-серверы, то есть серверы автоматизации, поддерживающие интерфейс IDataBroker (сейчас серверы MIDAS могут быть не только COM-, но и CORBA-серверами, но об этом будет рассказано в других статьях данного цикла). О популярности этого направления свидетельствует поистине огромное количество писем, поступившее в ответ на чуть ли не единственную, опубликованную более года назад в нашем журнале статью на эту тему, а также устойчивый спрос на консалтинговые услуги и коммерческие курсы, посвященные данной теме.
В настоящей работе не содержится детальных подробностей создания обычных MIDAS-серверов и клиентов; предполагается, что читатели с ними уже знакомы. Интересующиеся данным вопросом могут обратиться к статье «Создание серверов приложений с помощью Delphi 3» (КомпьютерПресс, № 12’1997, с. 106-113; эта статья также доступна на сайтах www.interface.ru и www.citroforum.ru). Отметим лишь, что при всей кажущейся простоте создания многозвенных систем с помощью рассматриваемой технологии при ее практическом использовании в промышленных масштабах могут возникнуть некоторые проблемы.
1.2. Проблемы эксплуатации COM-серверов и COM+
Разработчики COM-серверов при их создании и эксплуатации нередко сталкиваются с различными проблемами. В частности, при разработке COM-серверов для доступа к данным, обслуживающих несколько клиентов, следует позаботиться о поддержке нескольких соединений с базой данных и о возможности работы с несколькими потоками. Создание подобного кода с помощью удаленных модулей данных Delphi или C++Builder, содержащих компоненты TDatabase и TSession, не представляет особых сложностей. Однако при большом количестве обслуживаемых клиентов наличие подобного многопользовательского сервиса предъявляет серьезные требования к аппаратному обеспечению компьютера, на котором данный сервис функционирует. Поэтому нередко разработчики пытаются создать дополнительный код для осуществления совместного доступа многих клиентов к нескольким соединениям с базой данных, при этом число последних должно быть по возможности минимальным (обычно для такого разделения ресурсов используется термин «database connection pooling», и в комплекте поставки Delphi 4 Client/Server Suite имеется соответствующий пример).
При подключении очередного клиента к COM-серверу происходит создание обслуживающего его COM-объекта (например, удаленного модуля данных), который уничтожается при отключении клиента от сервера. В известном смысле такой объект является «личным» объектом данного клиента. Заметим, что создание серверных объектов по запросу клиента требует определенных ресурсов (оперативной памяти, времени), что становится актуальным в условиях реальной промышленной эксплуатации многозвенных систем, когда удаленные модули данных или иные подобные объекты обслуживают большие объемы данных из большого количества таблиц. Поэтому для экономии времени, затрачиваемого на создание и уничтожение таких СOM-объектов, имеет смысл создать дополнительный код, осуществляющий однократное создание нескольких подобных COM-объектов коллективного пользования и предоставляющий их на время обратившимся клиентам по их запросу.
Еще одна проблема, с которой сталкиваются разработчики приложений, предназначенных для работы с серверными СУБД, — обработка транзакций, представляющих собой изменение данных в нескольких таблицах, которые все вместе либо выполняются, либо отменяются. Нередко код, описывающий транзакцию в стандартной двухзвенной клиент-серверной системе, содержится в клиентском приложении, а не в серверной части просто потому, что в случае отката транзакции клиентское приложение должно быть уведомлено об этом. Что касается распределенных транзакций, использующих синхронные изменения в нескольких различных базах данных, их обработка практически всегда производится только в клиентском приложении. Подобные требования усложняют написание клиентских приложений, особенно если в информационной системе их несколько, и повышают соответствующие требования к аппаратной части рабочих станций. Нередко с целью изъятия кода обработки транзакций из клиентского приложения разработчики создают специализированные сервисы, ответственные за обработку транзакций (так называемые мониторы транзакций; есть и специальные продукты, предназначенные для управления распределенными транзакциями).
Существует также ряд проблем, связанных с авторизованным доступом пользователей к сервисам, предоставляемым COM-серверами. Эти вопросы, если рассматривать их в рамках традиционной COM-технологии, находятся исключительно на совести разработчиков этих сервисов, а также системных администраторов, конфигурирующих DCOM. Спецификация COM не содержит никаких требований на этот счет.
Таким образом, существует потребность в расширении COM-технологии за счет сервиса, обеспечивающего создание COM-объектов для совместного использования многими клиентами, авторизованный доступ к этим объектам, а также, при необходимости, обработку транзакций этими объектами. Расширенная подобным образом технология COM получила название COM+, а сам сервис, реализующий это расширение, называется Microsoft Transaction Server (MTS).
Итак, Microsoft Transaction Server представляет собой сервис, обеспечивающий централизацию использования серверов автоматизации, а также управление транзакциями и совместное использование несколькими клиентами соединений с базой данных независимо от реализации сервера. Версия 2.0 этого сервиса входит в состав NT Option Pack (его можно получить на Web-сайте Microsoft) и доступна для Windows NT и Windows 95/98. Однако некоторые возможности MTS (например, управление удаленными объектами) реализованы только в версии NT Option Pack для Windows NT.
Отметим, что помимо создания COM-объектов для коллективного пользования, предоставления сервисов авторизации пользователя при доступе к объектам и обработки транзакций MTS предоставляет средства мониторинга объектов и транзакций, что упрощает их реализацию.
2. Как работает MTS
Серверы MTS создаются точно так же, как и обычные внутренние серверы автоматизации. Иными словами, они представляют собой динамически загружаемые библиотеки, реализующие интерфейс IDispatch. Однако регистрация таких объектов происходит по-другому.
Клиенты могут находить обычные COM-серверы, только если последние зарегистрированы в реестре Windows; в этом случае местоположение исполняемого файла или библиотеки, содержащей его реализацию, определяется путем поиска в реестре записи, которая содержит идентификатор (GUID) данного сервера. Если же COM-сервер выполняется под управлением MTS, то он регистрируется не непосредственно в реестре, а в окружении MTS. Клиент при этом взаимодействует с исполняемым файлом mtx.exe как с локальным или удаленным сервером автоматизации.
Серверные объекты могут быть объединены в так называемые пакеты (packages). Пакеты бывают двух типов: Library package (выполняются в адресном пространстве породившего такой пакет клиента; естественно, в этом случае удаленный запуск подобного пакета невозможен) и Server package (выполняются внутри отдельного процесса; в этом случае возможен их удаленный запуск).
2.1. Управление транзакциями
Для управления распределенными транзакциями используется специальный сервис — Microsoft Distributed Transaction Coordinator (MS DTC), который может быть активизирован либо с помощью Windows Control Panel, либо непосредственно из MTS Explorer.
Если объект MTS должен существовать внутри транзакции, MTS автоматически начинает ее при создании объекта. Некоторые объекты могут завершить или откатить транзакцию, и в случае удачного завершения транзакции объектом MTS инициирует завершение транзакции в базе данных. В случае отката транзакции объектом MTS инициирует откат транзакции в базе данных. При использовании такого механизма контроля транзакций клиентское приложение может не содержать ни кода, связанного с завершением или откатом транзакций, ни кода, отвечающего за соединение с базой данных, — этот код обычно содержится в объектах MTS.
Отметим, что объекты MTS могут инициировать создание других объектов (назовем их дочерними объектами). При этом, если в дочернем объекте генерируется исключение при выполнении каких-либо его методов, MTS информируется о необходимости отката транзакции. Объект-родитель при этом не обязан контролировать успешность выполнения операций дочерним объектом, так как в случае исключения в дочернем объекте MTS сам инициирует откат транзакции, порожденной родительским объектом, и уведомит об этом клиента. Благодаря такому механизму уведомлений и реализуется возможность управления распределенными транзакциями, то есть согласованными изменениями данных в таблицах, принадлежащих к разным базам данных, — в этом случае код, отвечающий за работу с этими таблицами, следует поместить в разные объекты MTS и инициировать их запуск из какого-либо другого объекта. Иными словами, на базе MTS возможно создание так называемых мониторов транзакций.
2.2. Вопросы безопасности
MTS позволяет использовать список пользователей и групп пользователей Windows NT в качестве списка пользователей своих объектов. При этом для каждого объекта можно установить правила его эксплуатации различными пользователями и группами пользователей. Помимо этого MTS поддерживает также механизм ролей, приблизительно аналогичный ролям некоторых серверных СУБД (роль представляет собой совокупность пользовательских прав и привилегий на использование тех или иных объектов и может быть присвоена пользователю или группе пользователей). Соответственно, при использовании MTS можно не включать код реализации правил безопасности в серверные объекты.
2.3. Коллективное использование объектов (object pooling)
Так как при использовании MTS код, отвечающий за соединения с базами данных, обычно содержится в объектах MTS, иногда бывает полезно инициировать создание нескольких экземпляров таких объектов для последующего использования их по запросу клиентов (MTS допускает такой режим использования своих объектов). В этом случае снижается сетевой трафик между MTS как клиентом СУБД и сервером баз данных за счет снижения частоты установки и разрыва соединений с сервером.
Нередко бывает, что серверные объекты при их создании потребляют немалый объем иных ресурсов, например, при создании больших временных файлов, сетевого трафика и т.д. Исходя из изложенных выше соображений такие типы объектов должны быть созданы однократно в заранее определенном количестве с целью их последующего использования клиентскими приложениями. Если в какой-то момент число клиентов, которым требуется такой серверный объект, превысит имеющееся количество, должны быть созданы дополнительные экземпляры, добавляемые к уже имеющемуся набору. Уничтожение дополнительных экземпляров ставших ненужными объектов должно производиться в соответствии с установленным для них заранее максимальным временем существования в неактивном состоянии.
Для реализации коллективного использования объектов используются специальные объекты, которые называются resource dispencers (слово dispenser означает «раздаточное устройство», или «распределитель»). Эти объекты фактически кэшируют ресурсы так, что компоненты, находящиеся в одном пакете, могут использовать их совместно. Из объектов подобного рода следует особо отметить BDE resource dispenser и Shared property manager.
BDE resource dispenser — это объект, устанавливаемый вместе с Delphi 4 и регистрируемый программой установки Delphi в среде MTS. Он управляет коллективным использованием соединений с базами данных, использующих BDE.
Shared property manager — это объект, позволяющий использовать общие свойства для нескольких различных серверных объектов.
3. Требования к объектам MTS
Серверные объекты MTS, как уже было сказано выше, представляют собой COM-серверы, выполняемые в адресном пространстве среды MTS и, следовательно, реализованные в виде динамически загружаемых библиотек (DLL).
Все компоненты MTS поддерживают специфический для них интерфейс IObjectControl, содержащий методы для активации и деактивации объекта MTS и управления ресурсами (в том числе соединениями с базами данных, рис. 1).
Как было сказано выше, компоненты MTS могут функционировать как COM-объекты вне адресного пространства клиента (out-of-process server). В этом случае клиентское приложение взаимодействует с созданным внутри его адресного пространства proxy-объектом, передающим запросы клиента с помощью вызовов удаленных процедур содержащемуся внутри адресного пространства сервера stub-объекту, взаимодействующему с компонентом MTS посредством его интерфейса. Информация о соединении содержится в proxy-объекте. Последний может активизировать и деактивировать MTS-объект, отдавая ресурсы другим клиентам, нуждающимся в данном сервисе, незаметно для клиента.
Компоненты MTS могут также функционировать как внутренние серверы автоматизации (in-process-сервер). В этом случае их удаленный запуск исключен.
Серверный объект MTS должен иметь стандартную фабрику классов и библиотеку типов (они автоматически создаются при использовании MTS Object wizard). Можно редактировать библиотеку типов, добавляя свойства и методы (в дальнейшем они будут использованы MTS Explorer для получения сведений о его объектах на этапе выполнения). Помимо этого компонент должен экспортировать функцию DllRegisterServer и осуществлять саморегистрацию его CLSID (идентификаторов класса сервиса), PogID (идентификаторов сервера), интерфейсов, библиотеки типов (MTS Object wizard автоматически генерирует соответствующий код).
Если предполагается коллективное использование серверного объекта, он не должен хранить внутри себя сведения о состоянии данных, связанных с конкретным клиентом (для этого используются термины «stateless code» и «stateless object»). Рассмотрим пример создания такого кода.
Использование Microsoft Transaction Server для управления распределенными транзакциями:
создание серверных объектов и их клиентов
4. Пример 1: создание простейшего серверного объекта
4.1. Предварительная подготовка
Прежде чем приступить к созданию серверных объектов, предназначенных для работы под управлением MTS, следует убедиться в том, что сам MTS и Delphi 4 установлены корректно. Во-первых, NT Option Pack, содержащий MTS, следует обязательно установить до Delphi. Тогда в процессе установки Delpi при обнаружении инсталляционной программой установленной копии MTS в него будет добавлен специальный пакет (package) BDE-MTS, содержащий объект BdeMTSDispenser (его можно обнаружить с помощью MTS Explorer, рис. 2).
Следует также убедиться, что данный объект поддерживает транзакции. C этой целью нужно из контекстного меню объекта BDEDispenser выбрать опцию Properties, а в появившейся диалоговой панели — закладку Transaction (рис. 3).
Далее следует запустить BDE Administrator, открыть страницу Configuration, выбрать раздел System/Init и установить значение параметра MTS POOLING равным TRUE. Только при этом значении данного параметра возможна поддержка транзакций и коллективное использование соединений с базами данных, доступных с помощью BDE (рис. 4).
Для выполнения описанных ниже примеров следует создать три таблицы в трех различных базах данных. Первая из них требует наличия сервера IB Database (он входит в комплект поставки Delphi 4) и должна быть создана в базе данных IBLOCAL с помощью следующего скрипта:
CREATE TABLE STOCKTABLE ( GOODSNAME CHAR(30), PRICE FLOAT, GOODSNUMBER INTEGER NOT NULL)
В этой таблице будут содержаться сведения о товарах на складе (название, цена, порядковый номер, являющийся также первичным ключом этой таблицы).
Для генерации первичных ключей в этой таблице создадим также генератор:
CREATE GENERATOR GEN1; SET GENERATOR GEN1 TO 5
Можно ввести в таблицу какие-либо данные (рис. 5):
Следующую таблицу создадим в формате Paradox (например, с помощью Database Desktop) и поместим в базу данных DBDEMOS, поставляемую вместе с Delphi. Структура этой таблицы приведена на рис. 6.
В этой таблице будут храниться данные о заказах на доставку товаров со склада (номер позиции на складе, название товара, адрес доставки).
И, наконец, третья таблица формата dBase должна быть создана в произвольном каталоге, и этот каталог должен быть описан как псевдоним PAYDB (рис. 7):
В этой таблице будут содержаться сведения об оплате за заказы (номер позиции на складе, стоимость товара, адрес для высылки счета).
Распределенные транзакции при использовании этих таблиц будут описывать выбор товара на складе и оформление заказа на доставку. При этом в таблице заказов выбранная запись удаляется, и в двух других таблицах появляется по одной записи с тем же значением первичного ключа.
4.2. Создание серверного объекта
Для создания серверного объекта следует со страницы Multitier репозитария объектов выбрать пиктограмму MTS Data Module (рис. 8).
Далее в появившейся диалоговой панели MTS Data Module Wizard следует ввести имя класса и выбрать способ работы с транзакциями (рис. 9).
После этого будет сгенерирована стандартная библиотека типов, связанная с созданным модулем данных.
В созданный модуль данных поместим один компонент TSession, один компонент TDatabase, один компонент TProvider, один компонент TTable и два компонента TQuery (рис. 10).
Свойство AutoSessionName компонента TSession установим равным True. Свойство SessionName компонента TDatabase установим равным имени компонента TSession (это делается для того, чтобы не было конфликтов между именами различных пользовательских сессий внутри процесса MTS при создании нескольких однотипных объектов). Свяжем компонент TDatabase с псевдонимом IBLOCAL, установив его свойство LoginPrompt равным False (вполне очевидно, что в серверном объекте диалог ввода пароля появляться не должен – ведь клиентское приложение, использующее его, может находиться на удаленном компьютере, рис. 11).
Свяжем компоненты TTable и TQuery с компонентом TDatabase и в качестве значения свойства TableName выберем имя вновь созданной таблицы STOCKTABLE. Свяжем компонент TProvider с компонентом TTable.
Далее установим значения свойств SQL компонентов TQuery:
insert into STOCKTABLE values(:a,:b,GEN_ID(GEN1,1))
и
delete from STOCKTABLE where GOODSNUMBER=:C
Первое из SQL-предложений добавляет запись в таблицу STOCKTABLE с автоматической генерацией первичного ключа. Второе удаляет запись на основе значения первичного ключа.
Обратите внимание: ни компонент TTable, ни компонент TProvider не следует экспортировать из модуля данных. Причина этого заключается в том, что подобные экспортированные объекты хранят состояние данных, с которыми работает конкретное клиентское приложение, поэтому при коллективном использовании таких объектов могут возникнуть коллизии. По этой причине сведения о состоянии данных для конкретных клиентов хранятся менеджером разделяемых свойств MTS (MTS shared property manager), а в модулях данных между вызовами методов эти сведения присутствовать не должны. Поэтому вместо экспорта объектов из модуля данных мы создадим метод GetGoods, предоставляющий эти данные клиентскому приложению по его запросу.
После этого можно отредактировать библиотеку типов. Добавим к ней методы GetGoods для передачи клиентскому приложению содержимого таблицы и методы AddGoods и DeleteGoods для выполнения запросов, содержащихся в компонентах TQuery (рис. 12).
Реализация созданных методов приведена ниже:
Прокомментируем приведенный выше код. Напомним, что мы не экспортировали компонент TProvider или компонент TTable из модуля данных, а вместо этого создали метод GetGoods, предоставляющий клиентскому приложению данные из таблицы динамически, что позволяет не хранить сведения о состоянии данных в серверном объекте. Метод GetGoods представляет собой так называемый stateless code, а сам модуль данных в этом случае представляет собой так называемый stateless object (об этом было рассказано выше). Именно отсутствие статических данных, связанных с конкретным клиентом, позволит в дальнейшем сделать этот модуль данных разделяемым ресурсом.
Вызов метода SetComplete внутри метода GetGoods означает, что модуль данных более не нуждается в хранении информации о состоянии и может быть деактивирован. Если модуль данных представляет собой одну из частей распределенной транзакции (пока это не так, но чуть позже он станет одной из подобных частей), этот метод означает, что данная часть транзакции может быть завершена (естественно, при условии, что все другие части этой транзакции также могут быть завершены; в противном случае произойдет откат транзакции, в том числе и данной части). Если же MTS начинает транзакцию автоматически при создании модуля данных (опция Requires a transaction), вызов метода SetComplete приведет к попытке ее завершения.
Перед компиляцией проекта рекомендуется убедиться, что компоненты TDatabase, TSession, TTable неактивны.
Далее следует выбрать из меню Delphi опцию Run/Install MTS Objects. После этого следует выбрать или ввести имя пакета MTS (MTS package). После этого объект окажется зарегистрированным в MTS (рис. 13).
Следует обратить внимание на то, что регистрировать серверный объект как обычный COM-сервер не следует – в роли сервера с точки зрения реестра для клиента в данном случае выступает MTS, а не созданная библиотека.
Могут возникнуть проблемы при попытках внесения неоднократных изменений в код серверного объекта и запуска сервера с помощью собственно MTS или обращающихся к нему клиентов. В частности, может оказаться, что при попытке компиляции библиотеки появляется сообщение о невозможности создания выходного файла. Это может быть связано с тем, что какие-то экземпляры объекта уже созданы в адресном пространстве MTS, поэтому файл оказался заблокированным. В этом случае следует в MTS Explorer найти соответствующий пакет и из его контекстного меню выбрать опцию Shut down. Можно также выбрать в MTS Explorer раздел My Computer и из его контекстного меню выбрать опцию Shut down server processes, прекратив таким образом существование всех серверных объектов. Кроме того, можно уменьшить время существования серверного объекта в неактивном состоянии. Это делается с помощью выбора пункта контекстного меню Properties соответствующего пакета и установки необходимого значения свойства Shut down after being idle for… на странице Advanced появившейся диалоговой панели.
4.3. Создание клиентского приложения
Зарегистрировав созданный серверный объект в MTS, можно приступить к созданию клиентского приложения. Добавим в имеющуюся программную группу новый проект (или просто создадим его). На главную форму будущего приложения поместим компоненты TDCOMConnection, TClientDataSet, TDataSourse, TDBGrid, два компонента TEdit, два компонента TLabel и три кнопки (рис. 14).
В качестве свойства ServerName компонента TDCOMConnection выберем имя только что созданного нами серверного объекта (оно будет доступно, если объект зарегистрирован в MTS и при его выборе свойство GUID будет установлено автоматически). Если же клиент разрабатывается на удаленном компьютере, следует заполнить свойства GUID и ComputerName, причем в качестве свойства GUID следует выбирать не идентификатор сервера, а идентификатор соответствующего класса объектов – так называемый CoClass GUID. Причина этого очевидна – в общем случае динамически загружаемая библиотека может содержать несколько классов серверных объектов.
Свяжем компонент TClientDataSet с компонентом TDCOMConnection, выбрав его свойство RemoteServer из единственной позиции выпадающего списка. Свойство ProviderName оставим пустым – ведь при создании сервера мы не экспортировали никаких объектов. Далее свяжем компонент TDataSource с компонентом TClientDataSet и, наконец, компонент TDBGrid с компонентом TDataSource. Убедимся, что все невизуальные компоненты неактивны – до возникновения реальной необходимости получения каких-либо данных серверные объекты не должны создаваться, поэтому установка свойств Active или Connected должна быть произведена на этапе выполнения.
Закончив проектирование формы, создадим обработчики событий, связанных с нажатием на кнопки:
Запустив клиентское приложение, протестируем сервер, попытавшись добавить или удалить записи (заодно проверим правильность текста созданных нами SQL-запросов). Обратите внимание: для контроля изменений в базе данных следует нажимать на кнопку Connect & Refresh – обработчик соответствующего события вызывает серверный метод, выполняющий выгрузку данных из таблицы и передачу их клиентскому приложению (рис. 15).
5. Отладка серверных объектов MTS
Отладка серверных объектов MTS может быть осуществлена при наличии клиента, вызывающего отлаживаемые методы.
Для отладки серверного объекта следует открыть его в среде разработки, установить необходимые точки прерывания в его исходном тексте и выбрать из меню Delphi опцию Run/Parameters. Затем в строке Host application нужно указать местоположение исполняемого файла mtx.exe, например:
c:\winnt\system32\mtx.exe
(на Вашем компьютере это местоположение может быть другим). В строке Parameters следует указать имя пакета, в котором установлен данный объект:
/p:"Our Stock Package"
Обратите внимание: между двоеточием и кавычками не должно быть пробела (рис. 16).
Далее следует выбрать из меню опцию Run/Run. После этого можно запустить на выполнение клиентское приложение и вызывать из него отлаживаемые методы.
При попытке запуска сервера таким образом могут возникнуть следующие проблемы. Во-первых, может оказаться, что какие-то экземпляры объекта уже созданы в адресном пространстве MTS, и тогда, скорее всего, просто не получится запустить его в среде Delphi (или даже просто создать выходной файл). В этом случае следует в MTS Explorer найти соответствующий пакет и из его контекстного меню выбрать опцию Shut down.
Иногда не удается произвести отладочные операции, если отлаживаемый серверный объект должен выполняться в адресном пространстве клиента (опция Library Package страницы Activation диалоговой панели Properties пакета). В этом случае в поле Host application диалоговой панели Run/Parameters можно попытаться указать имя клиентского приложения.
Отметим, что, если клиентское приложение после старта серверного объекта не будет запущено, MTS может уничтожить созданный экземпляр серверного объекта по истечении времени существования объектов, определенных для данного пакета. По умолчанию оно равно трем минутам и может быть изменено с помощью опции Shut down after being idle for… страницы Advanced диалоговой панели Properties пакета.
Использование Microsoft Transaction Server для управления распределенными транзакциями:
создание и использование монитора транзакций
6. Пример 2: создание объектов для управления распределенными транзакциями
6.1. Создание серверных объектов для реализации распределенной транзакции
Теперь создадим второй объект для управления созданной ранее в базе данных DBDEMOS таблицей delivery.db. Закроем все открытые проекты и создадим новый серверный объект, такой же, как и предыдущий (рис. 17).
В отличие от предыдущего случая компонент TDatabase свяжем с базой данных DBDEMOS (рис. 18).
В качестве свойства SQL компонента TQuery используем следующее SQL-предложение:
delete from delivery where OrdNum=:d
Затем отредактируем библиотеку типов, добавив три метода GetDelivery, AddDelivery и DelDelivery для получения данных из таблицы, добавления и удаления записи (рис. 19).
Реализация этих методов имеет следующий вид:
Скомпилируем и установим данный объект в тот же пакет, что и в предыдущем случае.
Рекомендуется протестировать данный объект, создав клиентское приложение, более или менее аналогичное предыдущему. При тестировании следует помнить, что в этой таблице есть уникальный первичный ключ, поэтому при вводе записей с одинаковым значением поля OrdNum транзакции завершаться не будут.
И, наконец, создадим третий серверный объект, который будет управлять распределенными транзакциями и с этой целью порождать два предыдущих серверных объекта внутри своих транзакций. Вначале создадим объект, аналогичный двум предыдущим (рис. 20):
delete from ord where ordnum=:d
Теперь компонент TDatabase свяжем с созданной нами базой данных dbpay, содержащей таблицу ord.dbf (с ней мы свяжем компонент TTable, рис. 21).
Значение свойства SQL компонента TQuery будет выглядеть следующим образом:
delete from ord where ordnum=:d
Теперь добавим в проект библиотеки типов двух созданных ранее серверов. Для этого следует выбрать из меню Delphi опцию Project/Import type library, нажать кнопку Add и выбрать соответствующий файл с расширением *.tlb (рис. 22).
Далее отредактируем библиотеку типов данного серверного объекта, создав методы GetPays, AddPay, DelPay для доставки данных клиентскому приложению, добавления и удаления записей, а также метод DoTrans, реализующий распределенную транзакцию (удаление записи о выбранном товаре из таблицы STOCKTABLE в базе данных IBLOCAL и добавление по одной записи в таблицу заказов на доставку delivery.db в базе данных DBDEMOS и в таблицу счетов за заказы ord.dbf в базе данных paydb, рис. 23).
Реализация этих методов имеет следующий вид:
Прокомментируем приведенный выше код для метода DoTrans. Этот код реализует распределенную транзакцию, вызывая методы двух порожденных ею серверных объектов и выполняя собственные манипуляции с таблицей счетов. При вызове метода DoTrans клиентским приложением все три серверных объекта функционируют согласованно.
В начале выполнения создается так называемый контекст транзакции – интерфейс ITransactionContextEx. Этот интерфейс контролирует выполнение транзакции и обладает методами CreateInstance (создание экземпляра порожденного объекта), Commit (завершение транзакции) и Abort (откат транзакции). Отметим, что если для порождения серверного объекта MTS клиентским приложением используется компонент TDCOMConnection, то для порождения серверного объекта другим серверным объектом используется вызов метода CreateInstance интерфейса ITransactionContextEx. Параметрами этого метода являются CLSID объекта, интерфейс объекта и указатель на объект (возвращаемый параметр).
Далее следуют вызовы методов порожденных серверных объектов и собственные манипуляции с данными. Если все операции были успешными, транзакция завершена, и может быть выполнен метод Commit интерфейса ITransactionContextEx. Если же операции были неуспешными и в одном или в обоих порожденных серверах либо во время собственных манипуляций с данными возникнут исключения (например, другой пользователь уже удалил запись из списка товаров, сделав заказ, или какая-то из таблиц заблокирована), то будет вызван метод Abort.
Для тестирования распределенных транзакций установим все три серверных объекта в один и тот же пакет (рис. 24).
6.2. Создание клиентского приложения, использующего распределенные транзакции
Для тестирования созданного ранее сервера и инициализации распределенных транзакций создадим клиентское приложение, имитирующее процесс оформления заказов. На главной форме приложения поместим кнопку с надписью Connect, три компонента TDCOMConnection, связанные с соответствующими серверами, три компонента TClientDataSet, связанные с соответствующими компонентами TDCOMConnection, три компонента TDataSource, связанные с компонентами TClientDataSet, и блокнот из двух страниц. На одной из страниц блокнота разместим компонент TDBGrid, отображающий данные из таблицы со списком товаров на складе, компонент TEdit для ввода пользователем адреса доставки и кнопку для инициирования транзакции – принятия заказа. На второй странице поместим два компонента TDBGrid для отображения данных из двух других таблиц и компонент TSplitter — между ними (рис. 25).
Создадим обработчики событий, связанных с нажатием на кнопки:
Для тестирования распределенных транзакций запустим приложение. Введем адрес в компонент TEdit, выберем строку в списке товаров и нажмем на кнопку «Заказать» (рис. 26).
В результате получим сообщение о том, что заказ принят.
Нажав на кнопку Connect, обновим данные в компонентах TDBGrid. При этом запись, выбранная ранее, исчезнет, а в двух других компонентах TDBGrid появятся две новые (рис. 27).
Отметим, что, если не нажать на кнопку Connect, данные в компонентах TDBGrid останутся прежними (в настоящем примере не предусмотрено обновление данных после выполнения транзакции); у пользователя есть возможность попытаться повторно выбрать для заказа уже выбранную ранее запись (то есть заказать товар, заказ на который уже оформлен). В этом случае один из трех серверных объектов будет пытаться удалить уже удаленную запись (если вспомнить текст соответствующего SQL-запроса, она идентифицируется значением первичного ключа), и, значит, соответствующая часть транзакции не завершится. Соответственно, произойдет откат назад всей распределенной транзакции.
Сведения о завершенных и отмененных транзакциях можно получить, выбрав в MTS Explorer опцию Transaction Statistics (рис. 28).
Если вспомнить, что первичные ключи в нашей таблице товаров создаются с использованием генератора, созданного нами на сервере IB Database, становится очевидным, что создаваемые в этой таблице новые записи будут иметь значения первичного ключа, не совпадающие со значениями первичных ключей уже удаленных записей. Поэтому вероятность коллизий, связанных с удалением не той записи, в данном случае равна нулю.
Заметим, однако, что при создании подобного рода серверных объектов и их клиентов всегда следует пытаться исключить возможность ошибочных действий пользователя, поэтому более корректным было бы иметь следующий обработчик события, связанного с нажатием на кнопку «Заказать»:
Отметим также, что при нажатии на кнопку «Заказать» в случае отсутствия данных в компонентах TDBGrid в клиентском приложении возникнет исключение, связанное с отсутствием нужного поля в компоненте TClientDataSet. Следовательно, выбор данной кнопки в такой ситуации необходимо исключить. Поэтому установим значение ее свойства Enabled равным False и перепишем обработчик события, которое связано с использованием кнопки Connect::
Итак, мы создали три серверных объекта, реализующих распределенную транзакцию, связанную с удалением записи из одной таблицы и добавлением ее в две другие таблицы, а также клиентское приложение, инициирующее выполнение подобных транзакций. Следует обратить внимание на то, что таблицы, участвующие в транзакции, содержатся в трех физически разных базах данных.
Итак, на примере Delphi 4 и Microsoft Transaction Server мы рассмотрели возможность и способы использования технологий COM и COM+ для организации распределенных вычислений и управления распределенными транзакциями.
Следующая статья данного цикла будет посвящена другой технологии, используемой при организации распределенных вычислений, – DCE (Distributed Computing Environment), и ее реализации в многоплатформном сервере приложений Inprise Entera.
КомпьютерПресс 2'1999