Г.М.Ладыженский
Oracle CIS, (+7 095) 258-41-80 , glodigen@ru.oracle.com
В последнее время в компьютерной прессе появился ряд статей, посвященных менеджеру распределенных транзакций Tuxedo System и деталям его использования при создании информационных систем (ИС), имеющих архитектуру клиент-сервер. К сожалению, они не дают сколько-нибудь внятного представления об архитектуре Tuxedo System и о специфике применения этого в высшей степени тонкого инструмента. Наоборот, отсутствие систематизации в изложении и бездумное использование малопонятных терминов типа "пул серверов" искажает смысл Tuxedo System и создает превратное представление о месте и роли продукта. Чтение этих текстов и заставило автора взяться за перо и попытаться объяснить назначение Tuxedo и определить место этого продукта в разработке и управлении корпоративной ИС.
Напомним, что впервые информация о Tuxedo появилась в работе [1] в 1993 году. Тогда еще не было опыта практического использования системы и многое было просто непонятным, но интуитивно ощущались мощные идеи, лежащие в ее основе. В 1994 году, после практического изучения Tuxedo в Unix System Laboratories стали вырисовываться место и роль продукта в архитектуре ИС. Тогда же была написана статья [2], в которой автор попытался рассказать о нескольких моделях клиент-сервер и о мониторах транзакций как о ярких представителях программного обеспечения промежуточного слоя (middleware). Статья получила хорошие отзывы; в то же время стала очевидной необходимость применения Tuxedo в перспективных проектах.
Во многом это обусловлено объективной необходимостью перехода от монолитных систем класса "SQL-клиент - SQL-сервер" c двухзвенной архитектурой к более гибким системам клиент-сервер приложения-менеджер ресурсов" или "клиент-брокер-сервер", ключевым компонентом которых является программное обеспечение промежуточного слоя.
Детальное рассмотрение Tuxedo важно еще вот по какой причине. В настоящее время многие программисты приступили или планируют приступить к разработке систем клиент-сервер, важной частью которой является программирование межпроцессного взаимодействия. В большинстве случаев отечественные разработчики почему-то используют для этого низкоуровневый механизм сокетов, видимо, не подозревая о существовании множества систем, которые имеют промышленный высокоуровневый API межпроцессного взаимодействия, опирающийся на сокеты. Примером могут послужить интерфейсы прикладного программирования Tuxedo ATMI (основа Tuxedo System - элементы Tuxedo ATMI мы рассмотрим в этой статье) и Teknekron API (основа относительно новой для нашего рынка системы для создания распределенных приложений Teknekron).
Автор глубоко убежден в том, что бессмысленно пытаться программировать на низких уровнях (и тем самым "изобретать велосипед"), тем более в тех условиях, когда существуют множество стандартных высокоуровневых API, имеющих промышленный характер. В этом смысле создание современных информационных систем выглядит не как программирование в чистом виде (и, в особенности, не как программирование базовых компонентов), но как продуманное применение тщательно подобранных промышленных инструментов и средств, как конструирование единого целого из разнообразных готовых компонентов. В этом смысле Tuxedo System предлагается использовать в качестве каркаса информационной системы, на основе которого собираются другие элементы ИС. Это качество позволяет с полной уверенностью утверждать, что Tuxedo System может являться КЛЮЧЕВЫМ компонентом БОЛЬШИНСТВА корпоративных ИС.
Так вот, предлагаемая статья и обращена в первую очередь к проектировщикам и разработчикам ИС с целью предложить их вниманию объективную информацию о Tuxedo - с тем, чтобы дальше они могли решиться на использование системы (автору хорошо известно, что если коллектив программистов "не принимает" то или иное средство разработки, то доказывать его преимущества не только бессмысленно, но и вредно). Основная идея статьи - Tuxedo представляет собой не только и не столько продукт, сколько ТЕХНОЛОГИЮ создания ИС и в этом качестве предъявляет весьма высокие требования к квалификации, уровню мышления и общей профессиональной культуре специалиста.
Статья состоит из двух частей. В первой будет рассказано о внутреннем устройстве программной системы, функционирующей под управлением Tuxedo System, и дана краткая характеристика Tuxedo ATMI. Во второй части будут приведены конкретные примеры организации взаимодействия клиент-сервер и некоторые иллюстрации технологии применения Tuxedo System. При изложении пришлось пойти на ряд упрощений, так как рассмотрение деталей заняло бы в несколько раз больше места.
Несколько замечаний о терминологии. В настоящий момент вместо устаревшего "монитор транзакций" для определения Tuxedo используется термин "менеджер распределенных транзакций", что более соответствует профилю продукта. В то же время правильно было бы расширить это определения до "менеджер распределенных транзакций и коммуникаций" (ниже будет пояснено, почему это так). Говоря о Серверах Приложений, автор имеет в виду только программные компоненты (известно, что термин "сервер приложений" используется также и для определения компьютеров, на которых выполняется приложение, разделяемое многими пользователями).
Основные понятия
Вначале попытаемся упорядочить набор понятий. В основе Tuxedo System лежит трехзвенная или трехуровневая (three-tired) модель клиент-сервер, описанная в [2]. Центральное понятие модели - Приложение (Application), отличающееся от общепринятого. Приложение предоставляет осмысленный набор услуг, или Сервисов (Services), доступных для других Приложений. Так, например, Банковское Приложение предоставляет услуги "открыть счет", "зачислить на счет", "снять со счета" и т.д. Таким образом, внешне Приложение выглядит как завершенный набор Сервисов. С точки зрения внутреннего устройства, приложение - это набор Программ (возможно, взаимодействующих друг с другом), в рамках которых реализованы Сервисы. Реализованный Сервис - это некоторая Функция в терминах языка программирования, служащего для написания Программ. В рамках каждой Программы может быть реализован один или несколько Сервисов - это определяет разработчик Приложения.
Прикладная Система представлена набором Приложений, обращающихся друг к другу за услугами. Если одно Приложение запрашивает сервис другого, то первое считается Клиентом (Application Client - AC) по отношению ко второму, которое рассматривается как Сервер Приложения (Application Server - AS). Для того чтобы воспользоваться услугами Приложения, Клиент должен обратиться к Серверу Приложения, вызвав при этом Функцию Приложения, реализующую запрашиваемый Сервис. Для организации взаимодействия Приложений предусмотрен специализированный интерфейс прикладного программирования (Application Programming Interface - API). Для того чтобы вызвать из одного Приложения Сервис другого, необходимо использовать одну из нескольких десятков библиотечных функций, этот интерфейс предоставляющих.
Любое Приложение может оперировать с некоторыми информационными Ресурсами. Ресурсами могут быть Файлы, Базы данных, Списки или что-либо еще. Ресурсами управляет Менеджер Ресурсов (Resource Manager - RM). Приложение, желающее выполнить определенную операцию над Ресурсом, должно обратиться с запросом на некотором языке к соответствующему Менеджеру Ресурсов, который этот язык понимает. Для определенности далее мы будем рассматривать в качестве Ресурсов реляционные базы данных, Менеджера Ресурсов - реляционную СУБД, языка для описания доступа к базам данных - структурированный язык запросов SQL. Приложение может обращаться в рамках одной транзакции к нескольким СУБД, изменяя данные в нескольких базах данных. То, что изменения будут выполнены синхронно во всех базах данных или не выполнены вовсе (в случае ошибки), гарантирует Менеджер Транзакций.
Приложения в Прикладной Системе специализированы - каждое играет определенную роль. Выделим для определенности класс Приложений, которые обеспечивают интерфейс с пользователем Системы. В терминах трехзвенной модели клиент-сервер Приложения этого класса являются компонентом представления Системы (см.[2]). В то же время они же являются Клиентами по отношению к Серверам Приложений, реализующим операции прикладной области (см., например, выше - банковские операции). Приложения этого класса составляют прикладной компонент Системы. Для решения возложенных на них задач они манипулируют реляционными базами данных, пользуясь для этого услугами соответствующей реляционной СУБД. Для определенности будем далее везде называть приложения, обеспечивающие интерфейс с пользователем, Клиентами Приложения. Для взаимодействия Клиентов и Серверов Приложений используется интерфейс "Приложение-Менеджер Транзакций" (Application-Transaction Manager Interface - ATMI).
Рисунок 1.
В результате мы получаем трехзвенную архитектуру Системы (Рис. 1). Менеджер транзакций и коммуникаций (Transaction & Communication Manager - TCM) - это программная система, управляющая взаимодействием Клиентов Приложений, Серверов Приложений и Менеджеров Ресурсов (реляционных СУБД) и управляющая обработкой транзакций, определенных в Клиентах и/или Серверах Приложений. Менеджер транзакций и коммуникаций (или, для краткости, Менеджер Транзакций) предоставляет среду, в которой функционирует Система. Важная особенность (которую очень многие пока не представляют до конца) состоит в том, что ни Клиент, ни Сервер Приложений не обращаются непосредственно к Менеджеру Транзакций. Клиент, Сервер и Менеджер Ресурсов общаются посредством вызовов функций ATMI. В реализации каждой такой функции спрятан вызов Менеджера Транзакций. Таким образом, когда Клиент обращается к Серверу или Сервер обращается к Менеджеру Ресурсов, происходит "невидимый" вызов Менеджера Транзакций. Иными словами, любое взаимодействие трех субъектов ИС трехзвенной архитектуры происходит с ведома и при невидимом активном участии Менеджера Транзакций. Менеджер Транзакций Tuxedo называется Tuxedo System/T или просто System/T.
Архитектура Приложения
Содержательно, Приложение - это набор Сервисов. Структурно, Приложение в понимании Tuxedo - это не просто исполняемый код. Оно, как указывалось выше, состоит из нескольких взаимодействующих Программ, функционирующих в рамках отдельных процессов, называемых Серверами. Серверы объединяются в Группы. Приложение имеет набор параметров. Полное описание Приложения содержится в его Конфигурационном файле. Последний имеет текстовый формат и содержит несколько секций, в каждой из которых приводятся параметры и их значения. В Конфигурационном файле описаны узлы (компьютеры), по которым распределено Приложение; Группы процессов (Серверов); сами Серверы; Сервисы, доступные в Приложении; Менеджеры Ресурсов, с которыми работает Приложение; различные аспекты маршрутизации запросов; данные о приоритетах Сервисов; данные для балансирования загрузки и некоторые другие данные. Большинству приведенных понятий будет дано объяснение далее в статье; сейчас важно понять, что ВСЯ информация об архитектуре Приложения вынесена из кодов Приложения и сконцентрирована в Конфигурационном файле. Это - важнейший принцип Tuxedo System, позволяющий настраивать Приложение, не затрагивая его кодов. Приведем в качестве примера Конфигурационный файл простейшего Приложения, предоставляющего два Сервиса - REPORT и PRINT (пример Клиента этого Приложения будет рассмотрен ниже).
# Пример Конфигурационного файла # Приложения # Секция ресурсов Приложения: # IPCKEY-некоторое значение, # идентифицирующее выделенные # Приложению IPC-ресурсы # MASTER-идентификатор узла, # на котором выполняется Приложение # MAXACCESSERS-Число процессов, # которые могут одновременно # обращаться к Доске объявлений # MAXSERVERS-Общее число Серверов # Приложения (максимум-32768) # MAXSERVICES-Общее число Сервисов # Приложения (максимум-32768) # MODEL-модель Приложения: # SHM-выполняется на одном # компьютере, # MP-многомашинная конфигурация # LDBAL-Выполняется(Y) или не # выполняется(N) баланс загрузки *RESOURCES IPCKEY 62345 MASTER application MAXACCESSERS 5 MAXSERVERS 5 MAXSERVICES 10 MODEL SHM LDBAL N # Секция описания компьютеров- # узлов, на которых выполняется # Приложение # DEFAULT-параметры, общие для всей # секции # APPDIR-полное имя каталога, # содержащего исполняемые коды # Приложения # TUXCONFIG-полное имя бинарного # Конфигурационного файла # ROOTDIR-полное имя корневого # каталога System/T *MACHINES DEFAULT: APPDIR="/users/devel/appl" TUXCONFIG "/users/devel/appl/tuxconfig" ROOTDIR="/usr/local/tuxedo" # Сетевое имя компьютера elefant LMID=application # Секция описания группы Серверов # LMID-идентификатор узла, на # котором выполняется Приложение # GRPNO-номер группы # OPENINFO-строка, определяющая # вызываемый Приложением Менеджер # Ресурсов *GROUPS GROUP1 LIMID=application GRPNO=1 OPENINFO=NONE # Секция описания Серверов # DEFAULT-параметры, общие для всей # секции # CLOPT-определяет командную строку, # передаваемую процессу-Серверу # SRVGRP-имя группы # SRVID-идентификатор Сервера *SERVERS DEFAULT: CLOPT="-A" reportserver SRVGRP=GROUP1 SRVID=1 printserver SRVGRP=GROUP1 SRVID=2 # Секция описания Сервисов *SERVICES REPORT RPRINT
Для Приложения определены две основные операции: старт (выполняется утилитой tmboot()) и останов (выполняется утилитой tmshutdown()). Перед стартом Конфигурационный файл должен быть откомпилирован в бинарный формат. Компиляция выполняется утилитой tmloadcf(); в процессе компиляции проверяется корректность Конфигурационного файла и взаимное соответствие значений параметров. Старт Приложения происходит следующим образом. System/T запрашивает у UNIX необходимые Приложению IPC-ресурсы; загружает параметры Приложения из откомпилированного Конфигурационного файла в разделяемую память; размещает в разделяемой памяти служебные структуры, необходимые для работы Приложения; запускает служебные процессы System/T; запускает Серверы Приложения. Сегмент разделяемой памяти, где System/T размещает служебные структуры и параметры Приложения, носит несколько странно звучащее в данном контексте (в русском языке) название - Доска объявлений (Bulletin Board). В частности, System/T выставляет на Доску объявлений имена доступных в Приложении Сервисов. Доска объявлений обслуживается служебным процессом System/T под названием BBL (Bulletin Board Language).
Успешное завершение tmboot() означает, что Приложение стартовало и готово к работе и для Клиентов Приложения доступны его Сервисы. Отметим как важнейшую особенность, что Клиенты адресуют в своих запросах только Сервисы, не указывая Серверы, эти Сервисы содержащие. Внутреннее устройство Приложения невидимо Клиентам; более того, им невидимо и расположение Приложения - на каком из узлов сети оно выполняется; более того, им совершенно ничего не известно об используемых Приложением Менеджерах Ресурсов. Следовательно, можно считать, что между Клиентами и Сервисами System/T образует информационную шину; Клиенты направляют в нее запросы, адресуя в них конкретный Сервис; запрос передается по шине "куда-то"; ответ на запрос Клиент также получает из информационной шины (Рис. 1).
Стартовав, Приложение функционирует бесконечно - до тех пор, пока не будет выполнена утилита tmshutdown либо пока не будет остановлен компьютер, на котором Приложение выполняется. Работой активного Приложения можно управлять. Утилита tmadmin позволяет опрашивать параметры состояния Приложения - получать информацию об активных клиентах Приложения, об очередях сообщений, о загрузке сервисов. Более того, можно вмешиваться в работу Приложения, например приостанавливать выполнение отдельных Сервисов или переносить отдельные компоненты Приложения (Серверы, группы Серверов) или все Приложение целиком на дублирующий узел сети. Этот процесс называется миграцией. Начиная с версии 6.0, доступна утилита администрирования xtuxadm(), предоставляющая графический интерфейс (в рамках Motif) для доступа к параметрам Приложения и пришедшая на замену tmadmin.
Понятие миграции нуждается в пояснении. Дело в том, что для Приложения может быть определен так называемый "разделяемый" режим функционирования. В нем для Приложения определено два узла: основной и дублирующий (информация об этих узлах, в том числе и их сетевые адреса, заносится в Конфигурационный файл). При необходимости (например, если требуется вывести основной узел из эксплуатации на время профилактических работ) Администратор Приложения запускает процесс миграции всего Приложения или отдельных его компонентов (Серверов или их групп) на дублирующий узел. Важно, что миграция компонентов Приложения выполняется вместе с контекстом их окружения: набором объявленных Сервисов, очередями сообщений и т.д. Фактически, Доска объявлений основного узла в процессе миграции точно воспроизводится на узле дублирующем.
Важнейшая особенность Tuxedo System, пока, по сведениям автора, отсутствующая у других менеджеров транзакций (Encina, TopEnd) - возможность программного доступа к параметрам Приложения на Доске объявлений. Набор актуальных данных о Приложении (данные из Конфигурационного файла плюс оперативная информация о Приложении) называется Базой Данных Управления (Management Information Base - MIB). Доступ к MIB и оперативное изменение значений параметров Приложения могут осуществляться из программы, причем для обращения к параметрам Приложения в MIB используются стандартные вызовы библиотеки Tuxedo ATMI (tpcall(), etc). Описание интерфейса MIB имеется у автора статьи; опытным путем доказано, что программный интерфейс к MIB доступен, начиная с версии Tuxedo System 5 (хотя декларирован он, только начиная с версии 6). Пользуясь MIB и компонентой Tuxedo /Admin (также доступной, начиная с версии 6.0), разработчик может создать собственные программы мониторинга и диспетчеризации Приложения.
Типизированные буферы
Взаимодействие Клиента и Сервера невозможно без использования механизма структуризации и обмена данными. Обращаясь с запросом к Серверу, Клиент должен каким-то образом передавать ему исходные данные и получать результат. Tuxedo System позволяет использовать традиционные средства языка C - структуры. Вообще говоря, взаимодействие Клиента и Сервера можно организовать, передавая данные в структурах. Однако это - весьма ограниченный и негибкий подход. Дело в том, что любые изменения в структурах (добавление новых полей, исключение старых) требуют перекомпиляции программ, их использующих. Кроме того, если данные из структур хранятся в файлах, то такие изменения также требуют реорганизации файлов. Размер элементов структур и их смещение в структуре фиксированы - это влечет избыточный расход памяти, так как далеко не в каждый момент времени элементы структур содержат конкретные значения и очень часто в них находится "мусор".
В Tuxedo System разработан специальный механизм структуризации данных. Речь идет о структурированных или типизированных (fielded) буферах. Структурированный буфер представляет собой коллекцию значений именованных полей. Главные преимущества структурированных буферов перед структурами в терминах C - независимость данных от программы, гибкость и легкость модификации. Поля могут быть добавлены в буфер, удалены из него, может быть изменен размер поля - и все это делается без перекомпиляции программ, работающих с буфером. Доступ к буферам и их полям осуществляется посредством конструкций языка манипулирования полями (Field Manipulation Language - FML). FML реализован как библиотека функций, доступных из C-программ. Структурированные буферы Tuxedo часто для простоты называют FML-буферами - мы также будем использовать этот термин.
Первоначально FML-буферы были задуманы не только как стандартизованный метод представления данных для их передачи между взаимодействующими процессами (то есть между Клиентом и Сервером), но и как формат данных для их хранения и обработки Tuxedo System/D - специализированной системой управления реляционными базами данных компонентом, который ранее входил в состав Tuxedo System, однако затем был исключен из системы. В настоящий момент FML-буфер - это унифицированное средство для представления данных в системе с архитектурой клиент-сервер. Клиент и Сервер, взаимодействуя друг с другом, манипулируют с FML-буферами и передают через них данные. FML-библиотека содержит три группы функций. Первая включает функции создания и обновления буферов и выборки из них данных; во вторую входят функции преобразования типов данных в FML-буферах; третья состоит из функций преобразования данных из FML-буферов в структуры C. Последняя группа функций нуждается в пояснении.
Если для передачи данных между Клиентом и Сервером используются FML-буферы, то работу программ с данными можно организовать двумя способами. Первый предполагает для доступа к данным в FML-буфере использовать исключительно функции первой группы - данные в буфере доступны программе только через них (например, функция Fget() позволяет получить из FML-буфера значение поля с указанным именем). Второй способ позволяет преобразовать структуры C в FML-буферы и обратно. Для этой цели используется так называемые VIEWS-функции (мы не будем рассматривать их в этой статье из-за недостатка места).
Если мы хотим использовать для транспортировки данных FML-буфер, то мы должны определить его в текстовом файле (пример представлен ниже). Нетрудно увидеть, что описание FML-буфера схоже с описанием таблицы в реляционных базах данных. Для каждого поля FML-буфера определяется его имя, по которому к полю будет осуществляться доступ (колонка name), относительный номер поля в буфере (колонка rel-number), тип поля (колонка type). Колонка comment предназначена для комментариев, колонка flags предназначена для использования в будущем. Любое поле может иметь один из базовых типов: char, short, long, float, double, string, carray (массив символов).
#name | rel-number | type | flags | commnts |
EMPNAME | 1 | string | - | emp name |
EMPID | 2 | long | - | emp id |
EMPJOB | 3 | char | - | job type |
EMPADDR | 4 | string | - | street address |
EMPCITY | 5 | string | - | city |
EMPSTATE | 6 | string | - | state |
EMPZIP | 7 | string | - | zip code |
Вообще говоря, описания FML-буфера в текстовом файле достаточно для того, чтобы Клиент и Сервер обращались к соответствующему FML-буферу и размещали в нем данные и выбирали их из него. Для этого только должны быть определены две переменные окружения - FLDTBLDIR (имя каталога, где лежит файл с описанием FML-буфера) и FILETBLS (имя самого файла). Если же мы хотим иметь описания полей непосредственно в тексте программы-Клиента или программы-Сервера, мы можем воспользоваться утилитой mkfldhdr(), которая конвертирует описание буфера в файл <имя файла описания буфера>.h. Полученный файл содержит для каждого поля буфера следующую строку:
#define fname fieldid
(где значения fname, fieldid извлекаются из файла описания буфера) и может быть использован в программе (Клиент, Сервер) для последующего препроцессирования компилятором C.
FML-буферы - мощное и удобное унифицированное средство для транспортировки данных между Клиентом и Сервером. В большинстве Приложений на основе Tuxedo применяются преимущественно FML-буферы. Кроме них, Tuxedo позволяет работать с буферами типа STRING (буфер содержит строку символов, заканчивающуюся NULL) и CARRAY (буфер содержит массив символов, каждый из которых может быть нулевым). Буфер типа VIEW используют для двух целей - либо для преобразования FML-буферов в структуры языка C и обратно, либо непосредственно для передачи содержимого структуры C между Клиентом и Сервером. Прежде чем использовать буфер в программе, необходимо выделить под него память при помощи вызова tpalloc() (см. ниже). Функция userlog() записывает сообщение об ошибке в журнал пользователя (текстовый файл).
... FBFR *fbfr; /* указатель на FML-буфер */ ... if ((fbfr = (FBFR *)tpalloc("FML", NULL, Fneded(f,v))) == NULL) { (void)userlog("s%: Ошибка при выделении памяти под буфер"); exit(1); }
Tuxedo ATMI
Для описания взаимодействия Клиента и Сервера используется интерфейс прикладного программирования Tuxedo ATMI (функции ATMI перечислены в таблице 1).
Группа функций |
Функция |
Описание |
Интерфейс Приложения | tpinit( ) tpterm( ) |
Подключить Клиента к System/T Терминировать связь с System/T |
Интерфейс управления буферами | tpallos( ) tprealloc( ) tpfree( ) tptypes( ) |
Выделить память под буфер Перераспределить память под буфер Освободить память, выделенную под буфер Получить тип буфера |
Интерфейс связи Клиента и Сервера | tpcall( ) tpacall( ) tpgetreply( ) tpcancel( ) tpgprio( ) tpsprio( ) |
Синхронный вызов Асинхронный вызов Получить ответ после асинхронного вызова Прервать управление связью Получить приоритет последнего запроса Установить приоритет следующего вызова |
Интерфейс диалогового режима взаимодействия Клиента и Сервера | tpconnect( ) tpdiscon( ) tpsend( ) tprecv( ) |
Начать диалог Завершить диалог Передать данные Получить данные |
Интерфейс незатребованных сообщений | tpnotify( ) tpbroadcast( ) tpsetunsol( ) tpchkunsol( ) |
Послать уведомление по идентификатору клиента Послать уведомление по имени клиента Указать функцию обработки незатребованных сообщений Проверить очередь сообщений |
Интерфейс управления транзакциями | tpbegin( ) tpcommit( ) tpabort( ) tpgetlev( ) |
Начать транзакцию Зафиксировать текущую транзакцию Прервать текущую транзакцию Опросить транзакционный режим |
Интерфейс маршрутизации запросов | tpreturn( ) tpforward( ) |
Завершить Сервис и вернуть управление Клиенту Перенаправить запрос другому Сервису |
Служебные функции | tpsvrinit( ) tpsvrdone( ) |
Инициализация серверов Терминирование серверов |
Интерфейс динамического обьединения Сервисов | tpadvertise( ) tpunadvertise( ) |
Выставить Сервис на Доску Объявлений Убрать Сервис с Доски Объявлений |
Интерфейс Менеджера Ресурсов | tpopen( ) tpclose( ) |
Открыть Менеджер Ресурсов Закрыть Менеджер Ресурсов |
Интерфейс Tuxedo System/Q | tpenqueue( ) tpdequeue( ) |
Направить запрос в очередь Извлечь запрос из очереди |
Таблица 1.
Интерфейс Tuxedo ATMI.
Как мы видим, Tuxedo ATMI прост и обозрим - всего 34 функции, чем Tuxedo отличается от системы Encina, предоставляющей около 200 функций. Кратко поясним группы вызовов. Интерфейс Приложения включает две функции - tpinit() и tpterm(), которые будут описаны ниже. Интерфейс управления буферами обеспечивает основные операции с типизированными буферами (отметим, что операции над FML-буферами выделены в особую библиотеку и не включены в Tuxedo ATMI).
Одна из особенностей Tuxedo - возможность организации взаимодействия Клиента и Сервера в одном из нескольких режимов: синхронном, асинхронном, диалоговом, с хранимыми очередями, - возможны также модификации режимов; кроме того, Клиент и(или) Сервер может рассылать сообщения Клиентам в асинхронном режиме. Большое число режимов позволяет разработчику выбрать оптимальный для конкретной задачи режим и реализовать его с помощью функций ATMI.
Функции интерфейса взаимодействия Клиента и Сервера позволяют описать это взаимодействие в синхронном и асинхронном режиме, а также управлять приоритетами запросов (как это делается, будет рассказано ниже). Функции для организации диалогового (conversational) режима взаимодействия Клиента и Сервера выделены в особую группу.
Интерфейс незатребованных (unsolicited) сообщений требует особых комментариев. Дело в том, что в Tuxedo можно предусмотреть рассылку сообщений от Клиентов и (или) Серверов Приложений Клиентам (то есть инициатором сообщения может быть и Клиент, и Сервер, а вот получателем - только Клиент). Сообщения передаются в типизированных буферах и называются незатребованными - получатель сообщения реагирует на них асинхронно своим действиям. Можно организовать рассылку сообщений по именам Клиентов либо широковещательную рассылку всем Клиентам Приложения (вызов tpbroadcast()). Если необходимо послать (от Сервера) сообщение конкретному Клиенту (адресуя его по идентификатору Клиента), следует воспользоваться вызовом tpnotify().
Рассылка сообщений - отличный прием для уведомления Клиентов о всех событиях, происходящих в Приложении, в том числе (опосредовано) о всех событиях, происходящих в базе данных. Клиент реагирует на получение сообщения автоматически, передавая управление функции обработки сообщений, адрес которой был предварительно указан в программе с помощью вызова tpsetunsol(), либо периодически опрашивая свою очередь сообщений при помощи вызова tpchkunsol(). Способ реакции на сообщения напрямую зависит от операционной среды, в которой выполняется Клиент, и устанавливается значением одного из полей буфера инициализации Приложения.
Интерфейс управления транзакциями включает функции, определяющие начало (вызов tpbegin()) и успешное завершение (tpcommit()) транзакции - как локальной, так и распределенной (напомним, что транзакции в понимании Tuxedo - это обычные ACID-транзакции, краткое определение которых можно найти, например, в [4].) Функция tpabort() откатывает изменения, выполненные в рамках текущей транзакции запрошенными XA-совместимыми Менеджерами Ресурсов (например, SQL-серверами).
Интерфейс динамического объявления сервисов позволяет в процессе работы Приложения объявлять (делать доступными для Клиента) дополнительные Сервисы и связывать их с конкретными функциями (в терминах языка C). Дело в том, что каждому Приложению, как мы знаем, приписан некоторый набор Сервисов, имена которых заданы в Конфигурационном файле (секция *SERVICE ), а исполняемый код функций, ассоциированных с Сервисами, содержится в Серверах. Таким образом, изначально, после старта Приложения, мы имеем статичный набор Сервисов, причем информация о каждом Сервисе (имя Сервиса, адрес функции, ему приписанной и т.д.) содержится в строке Таблицы Сервисов, расположенной на Доске объявлений. Функция tpadvertise() добавляет новую строку в эту таблицу, то есть динамически изменяет список Сервисов Приложений, выставляя новый Сервис на Доску объявлений и приписывая ему конкретную C-функцию, после чего Сервис становится доступным для вызова. Функция tpunadvertise() удаляет запись о Сервисе из Таблицы Сервисов, делая обращение к нему невозможным.
Tuxedo System/Q - это специализированный Менеджер Ресурсов в составе Tuxedo System, управляющий хранимыми на диске очередями запросов. Помещение в очереди и выборка из них - прерогатива служебных процессов, которые запрашивают менеджер очередей для выполнения соответствующих действий.
Как и в других режимах взаимодействия, в запросе клиент адресуется к конкретному сервису. Для каждого из них на диске строятся собственные очередь запросов и очередь ответов. Помещением сообщений в первую и извлечением сообщений из второй ведает сервер TMQUEUE. Сервер TMQFORWARD выбирает сообщения из очереди запросов и помещает сообщения в очередь ответов.
Упрощенно работа с очередями выглядит следующим образом. Клиент посылает запрос конкретному сервису, направляя его серверу TMQUEUE (вызов tpenqueue()). Последний помещает сообщение в очередь запросов к данному сервису. Сервер TMQFORWARD извлекает сообщение из очереди запросов и направляет его сервису (вызов tpcall()). Последний, выполнив предписанные действия и сформировав ответ на запрос также в виде сообщения, посылает его в очередь ответов. Клиент, при помощи вызова tpdequeue(), запрашивает сервер TMQUEUE на получение сообщения из очереди ответов, что тот и выполняет.
Возможность хранения очередей сообщений в долговременной памяти позволяет говорить о почти стопроцентной надежности взаимодействия Клиента и Сервера. Даже в случае сбоя компьютера все сообщения с точностью до последнего сохраняются, а их обработка возобновляется с той точки, где произошел сбой. Любые действия с очередями помещаются в тело транзакции. Это обеспечивает полную надежность при доставке сообщений, позволяет доставлять сообщения с уведомлением о получении и реализовать множество полезных функций, необходимых в коммуникационных проектах.
Клиент и Сервер Приложения
Клиент и Сервер как программы на языке C имеют специфическую структуру, которая будет описана ниже.
Логика функционирования Клиента Приложения выглядит следующим образом. Клиент должен инициировать связь с Tuxedo System/T, передав ему сведения о себе, выделить динамическую память под исходный и результирующий буферы данных. Затем Клиент может обращаться к Серверу различными способами, которые будут описаны ниже. Завершив взаимодействие с Сервером, Клиент должен освободить память, выделенную под буферы, и терминировать связь с System/T. Сведения о себе Клиент передает в структуре (в терминах языка C), которая помещается в буфер инициализации и направляется Tuxedo System/T в момент инициации связи с Tuxedo System/T. Логика Клиента описана в псевдокодах:
main(){ выделить память под буфер инициализации; разместить информацию о клиенте в буфере инициализации; инициировать связь с Tuxedo System/T; выделить память под буферы данных; цикл пока (клиент не закончил взаимодействие с сервером){ принять ввод пользователя; направить запрос сервиса серверу; получить ответ на запрос; сообщить ответ пользователю; } освободить память под буферы данных; терминировать связь Клиента с System/T; }
Для того чтобы начать работу с Tuxedo System/T, Клиент должен подготовить буфер инициализации и инициировать связь с System/T, передав ему буфер. Буфер инициализации (тип TPINIT) определен в atmi.h и содержит информацию о программе-Клиенте: имя пользователя, запускающего программу, имя программы, имя группы программ, флаг, определяющий системные характеристики взаимодействия данного клиента с System/T, а также пароль клиента, который после передачи буфера System/T будет сверен с эталоном пароля из Конфигурационного файла Приложения. Таким образом, в начале сеанса работы Клиент должен выделить память под буфер инициализации; сформировать данные в буфере, запросив их, например, у пользователя; инициировать связь с System/T посредством вызова tpinit(). Фрагмент программы, выполняющей эти действия, представлен ниже. В последующих примерах, дабы не перегружать программы излишними подробностями, будем использовать этот фрагмент под названием "подключить Клиента к System/T".
... TPINIT *tpinfo; if (tpinfo =(TPINIT*)tpalloc \ ("TPINIT",(char *)NULL, \ TPINITNEED(8))) == \ (TPINIT*)NULL{ (void)userlog("%s: Ошибка при выделении памяти под буфер инициализации"); exit(1); } ... if(tpinit((TPINIT*)tpinfo)==-1){ (void)userlog("s%: Ошибка при подключении Клиента к System/T"); exit(1); }
Отметим, что, как только Клиент издал вызов tpinit(), System/T добавляет единицу к общему числу подключенных Клиентов. Как известно, Novell оформляет лицензии на Tuxedo в зависимости от числа пользователей (возможно приобретение Runtime-лицензии на 5, 10, 25, 35, 50, и т.д.) пользователей. В данном контексте пользователь - это процесс, подключившийся к System/T посредством вызова tpinit(). Ограничение на число подключаемых клиентов действует реально: если приобретен Tuxedo System Runtime для 25 пользователей и в настоящий момент подключено 25 Клиентов, то попытка подключения еще одного приведет к предупреждению о превышении допустимого числа Клиентов, а дополнительный клиент будет отвергнут. Отключение клиента от System/T (терминирование связи с System/T) выполняется вызовом tpterm(). Как только Клиент использовал этот вызов и он завершился успешно, System/T считает данного Клиента отключенным и уменьшает число подключенных клиентов на единицу. Фрагмент "терминировать связь Клиента с System/T" представлен ниже.
... if(tpterm()==-1){ (void)userlog("s%: Ошибка при отключении Клиента от System/T"); exit(1) } ...
Сервер устроен более сложно, нежели Клиент. Дело в том, что Сервер должен обслуживать запросы нескольких Клиентов. Они помещаются в очередь сообщений к Серверу, которая обслуживается Tuxedo System/T. Очередь сообщений организована по приоритетному принципу. System/T извлекает запрос из очереди и передает его тому Сервису Сервера, которому он был адресован (имя адресуемого Сервиса присутствует в структуре запроса).
Прежде чем начать извлечение запросов из очереди, System/T объявляет все Сервисы, которые доступны в данном Сервере. Для этого System/T обращается к Описанию Приложения, размещенному после загрузки из Конфигурационного файла в разделяемой памяти, и выставляет на Доску Объявлений имена Сервисов данного Сервера.
Сервисы имеют приоритеты - целые положительные числа (значение в диапазоне от 1 до 100). Приоритет определяет порядок извлечения запроса из очереди (первыми обрабатываются запросы к Сервисам, имеющим наивысший приоритет). Начальные значения приоритетов Сервисов задаются в Конфигурационном файле в секции Сервисов; далее они могут изменяться динамически либо посредством использования MIB, либо (для Сервиса, адресуемого в следующем за вызовом tpsetprio()) - при помощи вызова tpsprio(). Интуитивно ясно, что этот вызов может использоваться, если следующий запрос должен быть выполнен немедленно - и с этой целью устанавливается максимальный приоритет адресуемого в запросе Сервиса (после выполнения запроса приоритет Сервиса можно установить в исходное значение - разумеется, оно должно быть предварительно получено функцией tpgprio()). Приоритетность обслуживания запросов в очереди к Серверу отнюдь не означает, что запросы к Сервисам с низкими приоритетами будут бесконечно долго находится в ней - через некоторый промежуток времени System/T выполняет ревизию очереди, направляя на обработку Сервисам "засидевшиеся" в очереди процессы. Пример использования приоритетов запросов приведен ниже.
Код Сервера состоит из двух частей: прикладной и системной. Прикладная часть, представляющая собой реализацию Сервисов данного Сервера (как функций в терминологии языка C), пишется разработчиком Приложения. Системная часть Сервера (main-компонент) добавляется Tuxedo System/T на этапе препроцессирования исходного кода Сервера. Логика Сервера Приложения представлена ниже.
Системная часть: инициировать связь сервера с Tuxedo System/T; объявить имена сервисов; выделить память под буфер запроса; бесконечный цикл { проверить очередь сообщений; извлечь запрос из очереди; поместить запрос в буфер; направить запрос и управление сервису; получить ответ и управление от сервиса; } освободить память под буфер запроса; Прикладная часть: имя сервиса (...) { выполнить обработку запроса; сформировать результирующий буфер; вернуть управление main-компоненту; }
В качестве примера рассмотрим простейший режим взаимодействия Клиента и Сервера - синхронный. Клиент направляет запрос Сервису, передавая данные в буфере; Сервис обрабатывает переданные ему данные, помещает результат в буфер и возвращает его Клиенту в результирующем или в исходном буфере. Передача запроса выполняется функцией Tuxedo ATMI tpcall(). Ниже приведен простейший пример синхронного режима. Клиент запрашивает сервис "REPORT", получает запрошенный отчет в буфере rbuf и передает его сервису "PRINT", который и выполняет печать отчета. Вызов сервиса в синхронном режиме сходен с известным механизмом RPC (вызов удаленных процедур). Вызов сервиса считается синхронным, поскольку Клиент получает ответ на свой запрос немедленно в рамках того же вызова (то есть Клиент и Сервер работают синхронно).
Вызов tpcall() определен следующим образом:
int tpcall (svc, ibuf, ilen, obuf, olen, flags) char *svc, *ibuf, **obuf; long ilen, olen, flag;
svc - имя запрашиваемого Сервиса;
сам Сервис должен быть описан в Приложении, а его имя выставлено на Доску объявлений Приложения;
*ibuf - исходный буфер;
ilen - его размер;
**obuf - результирующий буфер;
olen - его размер;
flag - устанавливает дополнительные параметры взаимодействия Клиента и Сервера и обработки транзакций, по умолчанию должен быть установлен в NULL.
/*****************************/ /* Пример Клиента Приложения */ /*****************************/ #include#include "atmi.h" main(){ char *sbuf; /* исходный буфер */ char *rbuf; /* буфер для отчета */ long r1len, r2len, r3len; /* размер буферов */ Подключить Клиента к System/T; /* Выделить память под исходный буфер */ if((sbuf=tpalloc("STRING", NULL, 0))==NULL){ Терминировать связь с System/T; Завершить программу; } /* Выделить память под результирующий буфер */ if((rbuf=tpalloc("STRING", NULL, 0))==NULL){ Терминировать связь с System/T; Завершить программу; } /* Копировать в исходный буфер параметры запроса: REPORT - имя запрашиваемого отчета DBNAME - имя запрашиваемой базы данных */ (void) strcpy (sbuf, "REPORT=accrcv DBNAME=accounts"); /* Вычислить размер исходного буфера */ r1len=strlen(sbuf)+1; /* Запросить сервис "REPORT"-получить отчет в буфере */ if(tpcall("REPORT", sbuf, r1len, &rbuf, &r2len)==-1){ Вызвать функцию обработки ошибок; Освободить память, выделенную под буфера; Терминировать связь с System/T; Завершить программу; } /* Запросить сервис "PRINT"-направить отчет на печать */ if(tpcall("RPRINT", rbuf, r2len, &rbuf, &r3len)==-1){ Вызвать функцию обработки ошибок; Освободить память, выделенную под буфера; Терминировать связь с System/T; Завершить программу; } Освободить память, выделенную под буфера; Терминировать связь с System/T; }
/*****************************/ /* Пример Сервера Приложения */ /*****************************/ #include#include "atmi.h" char *roundrobin(); /* Сервис RPRINT */ RPRINT (pbuf) TPSVCINFO *pbuf; /* буфер инициализации */ { char pname[20]; /*имя принтера*/ char ocmd[30]; /*команда вывода на принтер*/ long rlen; /* размер буфера */ int prio; /* приоритет запроса */ FILE *lp_pipe; /*получить приоритет текущего запроса */ prio = tpgprio(); /* направить задание на печать */ /* с низким приоритетом на */ /* печать в фоновом режиме */ if(prio <= 20) (void)strcpy(pname, "bigjobs"); /* иначе, направить задание на печать */ /* на один из нескольких небольших */ /* лазерных принтеров */ else if (prio <= 60) (void)strcpy(pname, roundrobin()); /* направить задание на печать */ /* на зарезервированный скоростной */ /* лазерный принтер */ else (void)strcpy(pname, "hispeed"); (void)sprintf(ocmd, "lp- d%s, pname); lp_pipe = popen(ocmd, "w"); (void)fprintf(lp_pipe, "%s", pbuf->data); (void)pclose(lp_pipe); if((pbuf->flags & TPNOREPLY)) tpreturn (TPSUCCESS, 0, NULL, 0, 0); rlen = strlen(pname) + 1; pbuf->data = tprealloc(pbuf->data, rlen); (void)strcpy(pbuf->data, pname); tpreturn (TPSUCCESS, 0, pbuf->data, rlen, 0); } /* Выбрать имя принтера */ char *roundrobin(){ static char *printers[] = {"prn1", "prn2", "prn3", "prn4"}; static int p = 0; if (p > 3 ) p = 0; return(printers[p++]); }
Вторая часть статьи - в следующем номере журнала.
Литература
- Ладыженский Г.М. Система обработки распределенных транзакций Tuxedo. "Открытые Системы", весна 1993 года.
- Ладыженский Г.М. Технология "клиент-сервер" и мониторы транзакций. "Открытые Системы", лето 1994 года.
- Ладыженский Г.М. Системы управления базами данных- коротко о главном. Часть 4. "СУБД", N 4, 1995.