Проектирование и разработка подсистемы управления транзакциями для АСУД "ЕВФРАТ"

Механизмы управления транзакциями в СУБД. Обзор средств удаленного взаимодействия с объектами. Разработка подсистемы управления транзакциями. Практический анализ производительности подсистемы. Способы защиты пользователей от опасных и вредных факторов.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 14.03.2013
Размер файла 449,9 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Вариантом COM, позволяющим строить распределенные приложения, - является DCOM (Distributed Component Object Model). Она распространяет принципы вызова удаленных процедур на объектные приложения COM. В DCOM взаимодействие удаленных объектов базируется на спецификации Distributed Computing Environment Remote Procedure Call (DCE RPC). Среда скрывает от клиента детали сетевого взаимодействия. DCOM в отличие от RPC позволяет динамически связывать удаленные объекты: клиент может обратиться к серверу-объекту на фазе исполнения, даже если не располагает на этапе компиляции информацией об его свойствах. В частности, имеется возможность нахождения объектов на сервере. Информацию об объектах, доступных на сервере на этапе исполнения, клиент получает из специального хранилища метаданных об объектах (Type Library), используя механизм OLE Automation. За счет этого можно менять функциональность серверов, не внося существенных изменений в код клиентских компонентов программы. В модели DCOM допускается использовать созданную Microsoft модификацию языка описания интерфейсов IDL (Interface Definition Language) - DCE IDL, но он не играет важной роли и служит в основном для удобства описания объектов. Реальная интеграция объектов в DCOM происходит не на уровне абстрактных интерфейсов, а на уровне двоичного кода.

В Windows есть возможность группировать объекты DCOM в рамках транзакций. За это отвечает служба ОС Microsoft Transaction Server (MTS). Он предлагает все стандартные функции монитора транзакций. Важным свойством MTS является то, что свойства транзакционности задаются на уровне контейнера COM. (Кроме того, в Windows входит служба Distributed Transaction Service, отвечающая за координацию распределенных транзакций в базах данных.) MTS был одной из первых коммерческих систем, комбинировавших поддержку транзакций и компонентов. MTS лучше всего описывать как контейнер для транзакционных компонентов или сервер приложений. Приложения, управляемые MTS, представляют из себя наборы COM-компонентов, оформленных в виде динамически подключаемых DLL-библиотек (Dynamic Link Libraries). С технической точки зрения есть несколько сервисов ОС, контролирующих работу инициированных компонентов COM и отвечающих на исходящие от этих компонентов вызовы. Данные сервисы и обеспечивают автоматическую поддержку транзакций, обеспечение безопасности, образование пулов соединений к БД, поддержку потоков выполнения и управление состоянием объектов.

Среда DCOM/MTS удобна для создания распределенных приложений, но она намертво привязана к платформе Windows, что не всегда приемлемо. Важные проблемы, возникающие при построении Windows-приложений, - проблемы надежности, производительности и контроля версий используемых библиотек компонентов (и связанных с ними библиотек DLL).

Стоит заметить, что в связи с переходом на платформу Microsoft .Net для построения решений корпоративного класса технологию DCOM стоит рассматривать как унаследованную и учитывать лишь с точки зрения интеграции в новые корпоративные информационные системы большого числа написанных на ней приложений.

В середине 90-х годов прошлого столетия для построения распределенных приложений стала применяться технология CORBA (Common Object Request Broker Architecture - общая архитектура брокера объектных запросов). Она разделяет много идей, заложенных в COM и RPC, но использует их особым образом. Ключевым компонентом архитектуры CORBA является язык IDL, на котором описываются интерфейсы вызываемых серверных компонентов. На его базе компиляторами генерируются классы-«заглушки» (stubs) и «посредники» (proxy). Они задействуются соответственно программами на клиенте и сервере как локальные объекты, но на самом деле отвечают за посредничество в передаче обращений к удаленной системе. С их помощью обеспечивается также независимость от конкретного объектно-ориентированного языка. Для управления серверными объектами применяется брокер запросов (Object Request Broker, ORB). В числе прочего он позволяет клиенту получать сведения о доступных объектах и их свойствах. В отличие от DCE IDL, язык CORBA IDL поддерживает основные понятия объектно-ориентированной парадигмы (инкапсуляцию, полиморфизм и наследование). Есть также возможность динамического связывания удаленных объектов - ее реализует интерфейс динамического вызова DII (Dynamic Invocation Interface). Однако поддержка этого интерфейса - опциональна и зависит от реализации ORB.

Для CORBA-взаимодействий используется специальный протокол IIOP (Internet InterORB Protocol). Вся механика сетевых и объектных коммуникаций от клиента скрывается - брокер автоматизирует процессы передачи свойств объектов (marshaling/unmarshaling), технические сложности установления соединения и т.п. Однако использование специального протокола требует некоторых усилий по организации его транспорта через брандмауэры на границе корпоративной сети.

CORBA изначально была нацелена на кросс-платформенную поддержку. Ее реализации имеются для всех разновидностей Unix, Windows и многих других популярных ОС. В этом ее главное преимущество перед объектной моделью Microsoft. Но часто эти реализации сильно различаются, т.е. они не до конца интероперабельны. Стандарт определяет почти два десятка служб брокера запросов, из которых часто реализуется в брокерах лишь несколько ключевых. В числе наиболее известных на сегодня продуктов VisiBroker и Enterprise Server компании Borland, BEA Tuxedo и IONA Orbix.

Механизм динамического вызова CORBA сложен в реализации. А ввиду недостаточной интегрированности с традиционными средствами разработки он долго не мог получить широкого признания, на которое претендовали его создатели. Кроме того, ORB-системы, как и RPC, ориентированы на синхронную связь. На сегодняшний день CORBA имеет большое унаследованное значение, но новые распределенные приложения чаще разрабатываются на J2EE и Microsoft .NET.

В язык программирования и платформу Java изначально встраивались средства для синхронного удаленного вызова методов RMI (Remote Method Invocation), очень похожие на CORBA. Как и в CORBA, в RMI используются свои «заглушки» и «каркасы» (описания интерфейсов объектов), а так же брокер запросов RMI. Упрощенная последовательность шагов для связывания модулей программы выглядит примерно следующим образом. Сначала необходимо определить интерфейс дистанционно вызываемого объекта как производный от стандартного Java-интерфейса java.rmi.RemoteException, а затем реализовать методы этого интерфейса в нужном объекте. Затем на основе интерфейса удаленного доступа сгенерировать RMI-заглушку для клиента. Во время запуска серверного приложения необходимо зарегистрировать класс на сервере имен. Клиент с помощью службы имен JNDI получает ссылку на экземпляр удаленного объекта и преобразует ее к типу интерфейса удаленного объекта. Затем клиент обращается с полученным интерфейсным объектом, как с локальным. Класс-посредник должен при этом присутствовать в пакете клиентского приложения, т.к. он отвечает за преобразование вызовов и отсылку пакетов на удаленный сервер. В роли параметров вызываемых на клиенте методов могут выступать Java-объекты. Поддерживается передача как по ссылке, так и по значению. Посредник и RMI-сервер скрывают от программиста сложности сериализации и десериализации объектов-параметров. В этом смысле RMI аналогичен Microsoft .Net Remoting. В принципе, CORBA 2.3 также поддерживает данные механизмы, но использовать функционал CORBA гораздо сложнее. Еще одно сходство RMI и .NET Remoting состоит в возможности активизации по запросу клиента объекта, уже давно существующего на сервере. Для связи RMI использует собственный коммуникационный протокол JRMP. Обычно он работает поверх транспортного протокола TCP, но могут быть исключения, например сервер BEA WebLogic применяет фирменный протокол T3.

Важно отметить сближение CORBA и RMI. Брокер, удовлетворяющий стандарту CORBA 2.3.1, является частью Java 2 Platform, Standard Edition, v1.3, причем он тоже написан на Java, и потому является переносимым. Он может также взаимодействовать с другими CORBA 2.3.1-совместимыми брокерами. Но главное, брокер поддерживает одновременно и вызовы компонентов по протоколу IIOP, и вызовы по RMI. При этом можно организовать взаимодействие между двумя Java/RMI-платформами через IIOP, что часто предпочтительно с точки зрения туннелирования трафика через брандмауэр. Пользователь Java также избавлен от необходимости делать какие-либо дополнительные лицензионные платежи для использования CORBA. Поддержка CORBA есть и в J2EE, а стало быть, в серверах Web-приложений корпоративного класса, например WebLogic 8.1. Используя функцию RMI-IIOP, программист может при помощи RMI API обратиться к CORBA-северу. Это позволяет вызывать из компонента Enterprise Java Beans (EJB) удаленные объекты, написанные как на Java, так и на других CORBA-совместимых языках. Можно также вызывать объекты EJB из сторонних программ, воспользовавшись сервисами CORBA. Для этого необходимо сгенерировать их IDL-интерфейсы и интерфейсы-посредники.

BEA WebLogic предлагает еще одну новую технологию - динамические объекты-посредники, которые позволяют устранить необходимость генерации классов посредников на этапе разработки приложения. Они представляют собой сериализуемые классы, передаваемые через сеть клиенту, когда тот находит нужный объект путем запроса к RMI-реестру. Как и в случае со статическими proxy, клиент работает с ними, как с обычными локальными объектами. Посредники же сериализуют запросы клиента и переправляют их в WebLogic Server. Остается проблема, когда и как генерировать подобные динамические прокси-классы. Сервер Weblogic позволяет делать это во время исполнения, когда происходит регистрация удаленного объекта. Компилятор «на лету» выявляет реализованные классом интерфейсы, определенные как производные от интерфейса дистанционного доступа, и продуцирует весь необходимый дополнительный байт-код. Он также создает вспомогательный серверный байт-код, отвечающий за десериализацию запросов клиента и передачу их на исполнение методам реального объекта, а также сериализацию результатов и возврат их обратно посреднику клиента. Иначе говоря, все, что нужно для создания приложения, - это просто зарегистрировать в реестре объект, реализующий дистанционно доступный интерфейс. Надо сказать, что несмотря на подобные ухищрения, технология RMI оказалась слишком низкоуровневой и сложной для непосредственного построения на ее базе распределенных приложений и де-факто была скрыта Java-технологией корпоративных объектов Enterprise Java Beans (EJB), а именно их контейнерами.

Вторая важная технология связывания Java-приложений называется Java Messaging Service (JMS). Она предлагает обобщенную модель и набор интерфейсов для средств гарантированной доставки сообщений. JMS позволяет связать компоненты, работающие под управлением разных Java-машин асинхронным образом. Как правило, в качестве провайдера функций JMS служат какие-либо из существующих платформ MOM, например WebSphere MQ корпорации IBM или JES Message Queue Enterprise корпорации Sun Microsystems. Фактически она является модификацией технологии MOM, о которой речь пойдет ниже, в применении к платформе Java. Спецификация JMS обеспечивает универсальный Java API, позволяющий разработчикам писать приложения, не заботясь о том, какое конкретно транспортное ПО будет использоваться. JMS позволяет передать Java-объект целиком, не думая об его преобразовании в подходящий для передачи формат (средства сериализации). Но при этом она только определяет порядок доступа к корпоративной системе обмена сообщениями, а каждый производитель JMS-ПО самостоятельно разрабатывает инструменты для администрирования среды подобного обмена. Благодаря асинхронности JMS обеспечивает существенно большую независимость от обрывов канала связи, а также масштабируемость системы. В последних спецификациях появились требования к реализациям по распределению нагрузки, отказоустойчивости, обработки ошибок. JMS включен в J2EE как стандартный компонент начиная с версии 1.3. Современные реализации JMS опираются на понятие фабрик соединений и конечных адресатов. Имеющиеся средства администрирования позволяют привязать эти объекты к системе имен Java Naming Directory Interface (JNDI). Клиент может сделать запрос к серверу JNDI и получить по осмысленным для человека именам ссылки на фабрику соединений и пункт назначения сообщения. С помощью этих ссылок он создает сначала соединение, а затем и сессию связи с конкретным адресатом и начинает отправлять ему сообщения. Поддерживаются также и неявные схемы доставки - публикации - подписки, которые характерны для брокеров сообщений. Спецификация требует от провайдеров обеспечения поддержки многих функций, включая гарантированную доставку, временное хранение сообщений на постоянном носителе, отсылки групп сообщений (локальная транзакционность) и прочее. И эти их качества становятся типовыми. Однако большой люфт в методах реализации спецификации остается - например, приоритет обслуживания лишь предлагается учитывать и он может и не поддерживаться менеджером очередей. Аналогичным образом указание времени жизни сообщения, указываемое при его отправке, не является командой для менеджера очередей, и клиенту не запрещается принять устаревшее сообщение. В заключение стоит сказать, что основной трудностью при работе с Java-ориентированными продуктами естественно является требование использования языка Java и связанной с ним платформы исполнения байт-кода (виртуальной машины), что не всегда желательно.

Microsoft .NET Remoting - новейшая технология связывания распределенных приложений. Она обеспечивает вызов методов удаленных объектов, находящихся под управлением среды Microsoft .NET. Поддерживается множество услуг, таких, как активация удаленных объектов, контроль времени их существования, взаимодействие между удаленными объектами через различные транспортные каналы. Во многом .NET опирается на тот опыт, что был накоплен за много лет создания распределенных приложений на базе RPC, CORBA, DCOM и EJB. В архитектуре .NET Remoting также выделяют клиентскую и серверную машину. Сервером является хост, отвечающий на запрос, а клиентом - хост, его отправляющий. Однако в отличие, скажем, от EJB это разделение относительно условно - в любой момент роли могут быть изменены. В этом смысле она похожа на технологию Web-сервисов, которая отдельно рассматривается ниже. Как и в RPC, и CORBA, клиент локально работает только с классом-посредником (proxy). Этот класс генерируется средой одним из трех способов: либо на основе экземпляра кода удаленного класса, либо на основе класса, содержащего описания интерфейсов удаленного класса (но не код его логики), либо специальным инструментом на основе получаемого с дистанционного сервера описания Web-сервиса. Вкратце схема взаимодействия выглядит следующим образом: в сообщении, отсылаемом клиентом серверу, классы-посредники кодируют название вызываемого метода удаленного объекта и все входящие переменные. На сервере сообщение раскодируется (т.е. определяется название метода и все входящие параметры) и выполняется реальный метод. Чтобы передать какие-либо данные на сервер, достаточно вызвать метод класса-посредника в качестве аргументов метода, взяв эти данные. Например, можно передавать любые двоичные данные или строки текста. Сервер, в свою очередь, аналогичным образом может передать клиенту возвращаемое значение метода - оно будет раскодировано на клиенте через класс-посредник. В этом отношении .NET Remoting напоминает сериализацию Java-объектов, применяемую, в частности, в JMS.

Отличительной особенностью .NET Remoting является наличие нескольких четко разделенных слоев, отвечающих за сокрытие от пользователя сложностей дистанционного взаимодействия с объектом. Первым из них является слой «прозрачных» посредников (TransparentProxy), отвечающих за упаковку всех параметров удаленного вызова в объект-посылку IMessage. Сформированная посылка передается в «настоящий» посредник (RealProxy), который и передает ее на удаленный сервер. Все эти классы заранее заданы средой .NET, но пользователь может расширять класс RealProxy, например, для подключения своих средств безопасности. Для взаимодействия с коммуникационным слоем RealProxy опирается на каналы. В канале важны два элемента - формовщик (formatters) и транспортировщик (transport sink). Формовщик отвечает за преобразование сообщения IMessage в пакеты данных протокола высокого уровня. В стандартном варианте .NET содержится два формовщика: двоичный и SOAP. Транспортировщик решает проблему доставки этих пакетов на более низком сетевом уровне - для стандартной редакции .NET это TCP и SOAP. Таким образом, доступны два стандартных вида взаимодействия (пользователем могут быть описаны и иные): для взаимодействия по двоичному каналу через TCP и пересылки запросов к удаленным объектам в виде сообщений SOAP поверх HTTP. В первом случае достигается наибольшая производительность, но ценой утраты значительной части кросс-платформенности. Он подходит для построения приложений в локальных сетях. Во втором случае методы удаленного объекта видны как Web-сервисы, обращение к которым может происходить через Интернет и границу, определенную брандмауэром. При этом может использоваться любой транспортный протокол третьего уровня модели OSI, а не только IP, как в первом случае. Однако для его реализации требуются большие вычислительные и сетевые ресурсы, нежели для случая двоичной связи. Протокол, который обращается к серверному приложению для доступа к своим объектам, настраивается при его запуске. Сначала регистрируется канал связи, затем как подключенные к нему регистрируются доступные дистанционные объекты. При регистрации канала указывается транспортный протокол (TCP, HTTP и пр.), протокол доступа (двоичный, SOAP и другие) и номер порта, по которому сервер будет принимать запросы. В .NET различаются два типа объектов - активизируемые сервером и клиентом. Объекты, активизируемые клиентом, находятся под управлением менеджера контроля жизненного цикла, гарантирующего, что по истечении определенного срока действия объекта (срока аренды, в терминологии Microsoft) будет произведена необходимая очистка памяти. Среда предоставляет развитые средства для контроля за этой арендой, в том числе и со стороны самого объекта. Объекты, активизируемые сервером, делятся на объекты для однократного обращения (Single call) и «единичные» (Singleton). Экземпляры однократных объектов создаются в момент обращения к их методам, «единичные» объекты могут существовать долго и сохранять состояние между вызовами их методов. Все настройки свойств объектов (типы объектов, каналы, номера портов) можно сохранить в конфигурационном XML-файле, благодаря чему перестройка приложения становится совсем простой. Число типов объектов в .NET Remoting существенно меньше, чем в EJB. Например, отсутствуют Persistent-объекты, связанные с записями в базах данных. Но для модели вычислений, принятой в .NET, это не нужно. Нет также средств обеспечения транзакционности на уровне контейнера, наличествующих в серверах приложений EJB. Но часто подобные расширения создают лишь дополнительные сложности, не давая программистам никакой экономии сил. Например, создание и регистрация объекта EJB - очень трудоемкая вещь, а все, что нужно для создания дистанционно доступного класса .NET Remoting, - это унаследовать его от класса MarshalByRefObject. В заключение стоит отметить, что .NET Remoting совместим с иными технологиями удаленного доступа, например COM/DCOM (через средства самой Microsoft), а благодаря поддержке открытых стандартов, наподобие XML и SOAP, можно теоретически организовать взаимодействие с приложениями на других платформах, в первую очередь Java. Важной особенностью .NET Remoting является то, что она не ориентируется исключительно на синхронное взаимодействие, а при применении SOAP-транспорта настраивается и на асинхронный режим работы. Это означает, что она может быть применена в сочетании со средствами гарантированной доставки сообщений для обеспечения максимальной надежности системы в целом.

Особого внимания заслуживает технология Web-сервисов. Web-сервисы (WS) позиционируются в настоящее время как универсальная технология связывания существенно разнородных систем. В ее основе лежит несколько стандартов: XML для описания данных, SOAP для передачи информации с одних систем на другие, WSDL для описания сервисов (в том числе задания типов входных и выходных данных) и UDDI для хранения и предоставления по запросу WSDL-описаний. Для создания относительно несложных систем этих трех стандартов достаточно. Однако уже сколько-нибудь нетривиальные решения требуют использования таких вещей, как гарантированная асинхронная доставка сообщений, управление транзакциями, шифрование пересылаемых между системами данных и обеспечение их неподдельности. Все эти направления так или иначе родственны WS, и активно создается некоторая надстройка разных спецификаций, позволяющая вписать эти технологии в мир WS, наблюдается попытка сделать решение частной проблемы панацеей от всех бед, в том числе и для областей, для которых WS изначально предназначены не были.

В частности существуют транзакционные протоколы на основе WS. Концептуально они делятся на два широких класса -- атомарные транзакции и бизнес-транзакции. Атомарные транзакции -- это наборы операций, осуществляемые в рамках границ очень небольшого доверительного домена и имеющие свойство «все или ничего», характеризующиеся набором свойств ACID. Все действия в такой транзакции, осуществленные до операции ее утверждения, являются предварительными, т.е. не сохраняются на постоянном носителе и не видны другим процессам. Выделяют одно- и двухфазные транзакции. В первом случае имеется лишь один участник, во втором их несколько и требуется дополнительная «подготовительная фаза», когда координатор последовательно запрашивает всех ее участников, могут ли они выполнить транзакцию. Если от них всех присылается положительный ответ, то координатор утверждает все транзакции нижнего уровня. Если какая-то из транзакций не может быть выполнена, то все они отменяются. Подобный подход позволяет всем приложениям действовать скоординировано, не допуская возникновения несогласованных состояний других приложений и в то же время ничего о них не зная. Бизнес-транзакции обычно существенно отличаются по характеру от атомарных. Вкратце их особенности сводятся к следующему:

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

· Индивидуальные задачи могут завершаться так, что их данные становятся общедоступны до завершения бизнес-транзакции в целом. Результаты исполнения этих задач могут иметь влияние на внешние компьютерные системы;

· Ответ на пользовательский запрос может занять много времени. Действия в реальном мире -- одобрение человеком, сборка, производство или доставка -- могут иметь место еще до того, как послан электронный ответ;

· В случае, когда возникшая исключительная ситуация требует, чтобы результаты действия, предшествующие текущему шагу, были логически отменены, просто прекратить работу транзакции недостаточно. Для обращения систем в исходное состояние алгоритм обработки исключительной ситуации может потребовать исполнения какой-то бизнес-логики, например компенсационных операций для каждого из проделанных шагов. Здесь стоит заметить, что автоматическая компенсация может оказаться неисполнимой, -- ведь данные, зафиксированные на предыдущих шагах, уже могли использоваться другими процессами и дополнительно измениться;

· Участники бизнес-действия могут находиться в разных доменах, где требуется явное установление доверительных отношений.

2.1.6 Выводы

На основании проведенного анализа и обзора были сделаны следующие выводы. Из рассмотренных протоколов управления транзакциями классических СУБД у пессимистического подхода основным недостатком являются простои во время тупиков и растрата машинных ресурсов на их обнаружение и разрешение. Оптимистический подход бесполезно тратит машинные ресурсы на работу транзакций, которые позже будут анормально завершены из-за возникновения конфликтов, а также на работу на стадии проверки при завершении каждой транзакции. Экспериментальные результаты показывают, что в случае классических СУБД пессимистический подход обычно показывает лучшие результаты. Предполагается использовать этот протокол для разрабатываемой подсистемы управления транзакциями. Транзакции широко используются при работе с базами данных, но классы из пространства имен System.Transaction библиотеки классов .NET Framework позволяют выполнять транзакции с изменчивыми или находящимися в памяти объектами, такими как списки объектов. Если список поддерживает транзакции, объект добавляется или удаляется и транзакция завершается неудачей, то все операции со списком автоматически отменяются. Запись в списки, находящиеся в памяти может выполняться в той же транзакции, что и запись в базу данных, таким образом .NET Framework оказывается удобным средством для решения задачи данной работы.

Данные, с которыми работает автоматизированная система управления документами «ЕВФРАТ», хранятся в СУБД, которая поддерживает транзакции. Обращения к этим данным осуществляются посредством объектного API. При условии, что несколько транзакций не затрагивают взаимосвязанные объекты, они должны иметь возможность выполняться параллельно, что может быть достигнуто за счет использования нескольких потоков, однако API имеет свое ограничение: он не позволяет обращаться к одному объекту из разных потоков. Таким образом, нужен механизм, позволяющий реализовать многопоточную транзакционность над этим API, который будет организовывать работу потоков со своими объектами.

2.2 Постановка задач

Целью данного проекта является разработка подсистемы - программного модуля, занимающегося управлением транзакциями для автоматизированной системы управления документами «ЕВФРАТ». Подсистема должна обеспечивать выполнение следующих функций:

1. Создание транзакций;

2. Выполнение операций с хранилищем и файлами в рамках транзакции;

3. Возможность фиксации или отката транзакции пользователем;

4. Автоматический откат транзакции в случае возникновения ошибки;

5. Выполнение транзакций в заданном числе потоков, распределение транзакций, по потокам для параллельной обработки;

6. Количество потоков - настраивается.

Требования к характеристикам подсистемы:

1. Планирование выполнения периодических действий во время простоя потоков, а так же не реже, чем через заданный интервал времени во время работы потоков;

2. Возможность использования технологии xNika[13] при работе с данными.

В ходе разработки должны быть удовлетворены следующие ограничения: подсистема должна быть разработана для платформы Microsoft .NET Framework версии 3.5 sp1 и выше с использованием языка высокого уровня C#.

2.3 Разработка подсистемы управления транзакциями

Автоматизированная система управления документооборотом «ЕВФРАТ» - это система, обеспечивающая поддержку всего жизненного цикла работы с документами в электронном виде и позволяющая полностью реализовать технологии электронного документооборота на предприятии. Основные задачи, решаемые системой - автоматизация работы с входящей и исходящей корреспонденцией, организационно-распорядительными документами и договорами.

Жизненный цикл работы с документами в системе состоит из следующих фаз: создание, ввод, регистрация, движение, обработка, согласование и хранение. На его протяжении часто возникает необходимость совершать группы последовательных операций над документами, каждая из этих групп представляет собой логическую единицу работы с данными. Эта группа может быть выполнена целиком либо успешно, соблюдая целостность данных и независимо от других параллельно производимых операций, либо не может быть выполнена вообще, и тогда она не должна произвести никакого эффекта. Например, имеется документ, после регистрации которого должно создаться поручение. Если документ успешно зарегистрируется, но поручение по нему создать не получится, то необходимо отменить и регистрацию документа. В связи с этим основной задачей настоящей работы является создание инструментария, позволяющего оформлять любые группы операций с объектами системы в транзакции и управлять ими.

Архитектура АСУД «ЕВФРАТ» клиент-серверная: функциональная логика системы в основном сосредоточена на сервере приложений, в то время как клиентские приложения ориентированы на представление данных. Клиентское приложение обменивается пакетами с сервером через гарантированное TCP соединение. В настоящий момент в процессе выполнения функциональной части работы сервер приложений использует три фиксированных служебных потока (один поток ожидает соединения клиентов по TCP, второй обеспечивает передачу данных между сервером приложений и соединившимися клиентами, а третий периодически взаимодействует с хранилищем) и по одному потоку на каждый из активных сервисов[14].

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

Хранилище, используемое АСУД ЕВФРАТ, реализовано при помощи технологии xNika, состоящей из двух частей - серверной и клиентской. Серверная часть хранилища xNika реализует довольно низкий уровень абстракции, поэтому ее непосредственное использование потребовало бы написания вспомогательного кода, который реализован в клиентской библиотеке. Клиентская библиотека Хранилища xNika накладывает свои ограничения - с одной стороны не позволяет работать с одним объектом из разных потоков одновременно и имеет внутренний (уровня соединения) кэш, который не позволяет одновременно выполнять несколько независимых параллельных транзакций, но с другой стороны - позволяет работать с xml-данными сложной структуры, позволяет использовать объектно-ориентированный подход. Эти ограничения предполагается преодолеть за счет использования каждой транзакцией отдельного объекта соединения с хранилищем, тогда изменения, вносимые одной транзакцией, не будут мешать другим транзакциям.

2.3.1 Предлагаемая модель подсистемы управления транзакциями

Сервер приложений АСУД «ЕВФРАТ» основан на наборе библиотек, реализующих объектно-ориентированную среду выполнения, которая в свою очередь реализует над средой хранения объекты системы документооборота, такие как документы, файлы, и вспомогательную инфраструктуру, включающую подсистемы кэширования, обработки событий и прочее. В контексте данной среды предлагается реализовать блоки управления рабочими потоками и соединениями, на основе которых реализовать подсистему управления транзакциями.

Функциональность блока управления рабочими потоками заключается в управлении параллельной обработкой запросов - элементов работы. Элемент работы - это объект, содержащий параметры задания и реализующий определенный интерфейс с методами, вызываемыми для выполнения задания. Рабочий поток - это объект, содержащий очередь элементов работы и исполняющий их в рамках потока. Блок реализует пул рабочих потоков, в которых параллельно выполняется вся фактическая деятельность. Элементы работы могут помещаться, в зависимости от необходимости, как в конкретный рабочий поток, так и в поток, выбираемый на основании его текущего состояния и уровня занятости его очередей - предпочтение отдается простаивающим потокам или потокам с самой короткой очередью. Рабочие потоки обрабатывают свои очереди постоянно, в промежутках через заданный интервал времени вызывают обработку событий простоя и о выполнении каждого элемента работы происходит соответствующее уведомление. Количество одновременно работающих рабочих потоков настраивается при инициализации пула в зависимости от характеристик средств вычислительной техники. Однако при этом нужно учитывать, что установка количества рабочих потоков больше числа процессоров на компьютере может привести к снижению суммарной производительности.

Блок управления соединениями по мере необходимости создает в рабочих потоках соединения со средой хранения. Для каждого потока существует свой набор соединений, каждое из которых используется в том потоке, в котором оно было создано. Когда соединение создается и используется, оно помечается как занятое, когда оно освобождается - помечается соответственно.

Транзакция в среде выполнения - это объект, занимающийся управлением различными ресурсами, используемыми в процессе согласованного изменения данных. Один из этих ресурсов - рабочий поток, который выделяется для каждой транзакции и затем закрепляется за ней. Один рабочий поток может быть отведен нескольким транзакциям, но все действия, выполняемые в рамках транзакции, выполняются в одном рабочем потоке. Другие ресурсы - соединение с хранилищем xNika и временные файлы, которые были запрошены или загружены в процессе транзакции. Для обеспечения возможности одновременной обработки нескольких транзакций, подсистема управления транзакциями использует пул подключений независимых xNika таким образом, что каждая открытая транзакция использует свой экземпляр подключения. Это необходимо для того, чтобы внутренний кэш хранилища xNika был независимый в каждой транзакции и изменения, вносимые в кэш в процессе обработки одной транзакции, не мешали работе других транзакций[13,15,16]. При завершении транзакции вызывается фиксация соответствующего ей соединения, после чего происходит очистка КЭШа, соединение приобретает статус свободного и доступного для использования последующими транзакциями.

Для исключения «зависания» открытых и незавершенных транзакций задается максимальный срок простоя транзакции - время, в течение которого допускается отсутствие какой-либо деятельности в ее рамках. По истечении такого таймаута транзакция принудительно завершается: осуществляется ее откат.

Каждая транзакция имеет очередь элементов работы, аналогичную очереди рабочего потока. Элементы из этой очереди по одному попадают в очередь конкретного потока, фиксированного за транзакцией. После завершения выполнения очередного элемента, за исключением случая, когда элемент неудачно пытался выполнить блокировку ресурсов, происходит перенос следующего элемента работы транзакции в очередь ее потока для выполнения. Иначе, в случае безуспешной попытки блокировки, блокирующий элемент заново ставится в очередь рабочего потока. Если в процессе выполнения задания, в том числе фиксации, возникает ошибка, транзакция принудительно откатывается и генерируется исключение. Автоматический откат так же происходит при превышении транзакцией лимита работы в 1 час. Все действия, производимые транзакцией, включая фиксацию, откат и освобождение ресурсов, вызываемое в любом случае при ее завершении, происходят в отведенном ей рабочем потоке.

2.3.2 Модель производительности подсистемы

Предположим, имеется система с многоядерным процессором с количеством ядер равным n. Имеется некоторый набор из N операций, который должен быть выполнен. S операций из этого набора должны выполняться строго последовательно, в то время как оставшиеся P операций программно независимы друг от друга и могут выполняться параллельно на всех ядрах процессора. Пусть s = S/N - доля операций, выполняемых последовательно, а p (равное 1 - s) - доля операций, выполняемых параллельно. В случае применения одноядерного процессора время, требуемое для выполнения заданного набора операций, составляет:

(2.1)

Где IPS (Instructions Per Second) - количество операций, выполняемое в секунду. В случае использования n-ядерного процессора время, затрачиваемое на выполнение всего набора операций, составит:

(2.2)

Поскольку приростом производительности в данном случае можно считать уменьшение времени выполнения набора операций при использовании многоядерного процессора по отношению ко времени выполнения того же набора с использованием одноядерного процессора, прирост производительности составит:

(2.3)

Предлагаемая организация подсистемы управления транзакциями позволяет увеличить долю операций p, которые смогут выполниться одновременно, за счет постоянной занятости потоков и предпочтения транзакций, которым либо не нужна блокировка, либо ее успешно удается получить.

2.3.3 Практическая реализация подсистемы

Объектно-ориентированная среда выполнения как объект, создаваемый единожды при запуске сервера и существующий на протяжении всей его работы, является экземпляром класса EnvironmentServer. Конструктор класса EnvironmentServer принимает на вход два параметра - строка подключения к серверу БД и количество инициируемых потоков для обработки. В конструкторе создаются и сохраняются в памяти три других объекта: пул соединений - экземпляр ConnectionPool, пул потоков - экземпляр ThreadPool и менеджер блокировок BlockManager. Последний из объектов затем доступен через соответствующее свойство EnvironmentServer. Кроме того, осуществляется подписка на событие простоя пула потоков OnIdle с целью поиска транзакций, слишком долго простаивающих и осуществление их отката, если они еще не на стадии отката или фиксации. Метод OpenTransaction() объекта EnvironmentServer создает и возвращает объект транзакции Transaction. Этот объект так же сохраняется в списке открытых транзакций EnvironmentServer и осуществляется подписка на событие завершения транзакции Complete, чтобы удалить завершенную транзакцию из списка. Метод Stop() запрещает создание новых транзакций, ожидает завершения выполняющихся, затем принудительно осуществляет их откат и вызывает останов пула потоков.

Класс ConnectionPool - управляет соединениями со средой хранения xNika. В конструкторе класса инициируется словарь-классификатор соединений xNika, содержащий информацию об открытых соединениях и их состояниях для каждого из потоков. Состояние соединения может быть одно из двух: занятое и свободное. В классе ConnectionPool определены методы GetConnection() и FreeConnection(). Метод GetConnection() возвращает свободное соединение для текущего потока. В случае отсутствия свободного соединения создается новое. Возвращаемое соединение отмечается как занятое и остается таким до вызова метода FreeConnection(). В последний передается соединение, которое помечается методом как свободное.

Класс BlockManager хранит словарь активных блокировок. Метод TryEnterBlock () получает в качестве аргумента идентификатор блокировки - текстовую строку, которая ассоциируется методом в словаре блокировок с текущей транзакцией; затем происходит подписка на событие завершения транзакции Complete с целью очистки всех блокировок, внесенных ею. Если попытка блокировки была успешной, возвращается истина, иначе возвращается ложь, если какая-то транзакция уже осуществила данную блокировку. Метод IsInLock() получает в качестве аргументов транзакцию и идентификатор блокировки и проверяет, была ли уже внесена такая блокировка, возвращая соответствующее логическое значение.

Класс ThreadPool управляет пулом рабочих потоков WorkingThread. В конструктор класса передается количество потоков и максимальный интервал между обработкой событий простоя пула потоков. На основании первого параметра конструируются и далее хранятся в памяти до останова пула экземпляры WorkingThread, представляющие собой рабочие потоки, на событие OnIdle каждого из которых осуществляется подписка с целью периодического вызова обработки события OnIdle самого пула потоков, но не чаще, чем через минимальный интервал, по умолчанию равный 100 миллисекундам, или максимальному интервалу, если он меньше 100 миллисекунд. В свою очередь цикл, выполняющийся в каждом из рабочих потоков периодически генерирует события OnIdle своего потока с интервалом, не превышающим заданное максимальное значение. Таким образом, событие простоя пула потоков возникает когда любой из рабочих потоков входит в состояние простоя, но не чаще фиксированного значения, или если с момента обработки последнего события простоя прошло времени больше, чем было указано при конструировании объекта ThreadPool; оно может быть использовано для запуска фоновых задач. Свойство Stopping позволяет идентифицировать, если пул потоков находится в состоянии останова. Метод Stop() пула потоков выполняет останов всех запущенных рабочих потоков: сначала происходит отписка от их событий OnIdle, перевод потоков в состояние завершения и блокирование добавления новых задач, затем ожидается освобождение очередей всех этих потоков, после чего потоки завершаются и их объекты уничтожаются. Метод Push() добавляет задачу, передаваемую ему в качестве аргумента, для выполнения в очередь одного из рабочих потоков. Поток выбирается тот, который не находится в состоянии останова и имеет наименее загруженный статус (наивысший приоритет) и самую короткую очередь. Попытка воспользоваться методом во время останова пула потоков приводит к исключению.

Далее рассмотрим класс WorkingThread, описывающий рабочий поток и управляющий очередью задач WorkItemsQueue в рамках потока. Конструктор создает новый поток, в котором запускается основной цикл, выполняющий задания из очереди до останова потока. Цикл выполняет задания по мере появления в очереди, вызывая во время простоя через заданный минимальный и во время работы - через заданный максимальный интервал времени между событиями простоя их обработку. Существует четыре состояния, в которых может находиться поток, они перечислены в порядке увеличения приоритета с точки зрения предпочтения потока при выборе для помещения задания в его очередь: Stopping (останов), Starting (запуск), Working (выполнение заданий из очереди) и Idle (ожидание очередного задания). Текущее состояние потока доступно для чтения через свойство State. Статическое свойство Current определяет текущий поток, если вызов производится в рамках рабочего потока, используется классом транзакции Transaction. Свойство ManagedThreadId позволяет однозначно идентифицировать поток. Свойство Context возвращает контекст текущей выполняющейся в потоке задачи, последний может использоваться ею и каждый раз очищается по завершении ее выполнения. Свойство Сount определяет общее число задач в очереди потока, а свойство queue возвращает саму очередь WorkItemsQueue. Для останова потока в классе определены методы StartStop() и Stop(): первый блокирует добавление новых заданий, а второй - сигнализирует о завершении, останавливая цикл, выполняющийся в потоке и сам поток.

Очередь потока WorkItemsQueue хранит в памяти соответствующий объект очереди и имеет метод Push(), принимающий в качестве аргумента задание WorkItem, добавляющий его в очередь и генерирующий соответствующее событие. Метод TryGet() возвращает истину и очередное задание WorkItem для выполнения, если таковое имеется в очереди, иначе - ложь, в обоих случаях генерирует соответствующие события, если очередь оказывается пуста. Метод Block() запрещает добавление заданий, а свойство Blocked определяет, является ли очередь заблокированной. Метод Wait(), принимающий на вход интервал времени, ожидает в течение этого интервала появления в очереди заданий.

Объект WorkItem представляет собой задание и конструируется с указателем на метод и его параметрами, сигнализирует об активации, запуске и завершении задания, а так же перехватывает все исключения и позволяет их обрабатывать. В конструктор класса WorkItem в качестве указателя на метод удобно передавать так называемое лямбда-выражение - сокращенное объявление анонимного метода, ставшее возможным в C# 3.0. Для объявления последнего введены два делегата anywork и anyworkandret, соответственно второй используется, если нужно возвращать результат. Метод Do() запускает выполнение задачи, а асинхронный контроль выполнения позволяет осуществлять специальный сигнализатор AsyncWaitHandle. Результат выполнения задания WorkItem доступен через свойство Result.

Кроме обычных заданий WorkItem введены так же особые ожидающие блокировку задания CLockWorkItem. Класс CLockWorkItem наследует и расширяет функциональность WorkItem. В конструктор передается объект транзакции Transaction и строковый идентификатор блокировки, далее от имени этой транзакции осуществляется попытка заблокировать ресурсы, результат которой логического типа и затем доступен через свойство Result. Метод Copy() создает заново вызвавший объект и возвращает его как WorkItem.

Основной класс, предполагающий использование, как и EnvironmentServer, не только в рамках подсистемы, но и за ними, - Transaction, - занимается управлением транзакциями. Для конструирования объекта транзакции в классе реализован статический метод CreateInstance(), на вход которого передается объект сервера окружения EnvironmentServer, пул соединений ConnectionPool и пул рабочих потоков ThreadPool. Метод CreateInstance() возвращает сконструированный и проинициализированный объект Transaction и используется в методе OpenTransaction() сервера EnvironmentServer, не требующем параметров и таким образом инкапсулирующем подробности внутренней реализации. На низком уровне методом Push() объекта ThreadPool в один из рабочих потоков помещается задание WorkItem. Оно инициализирует и связывает с текущим потоком включающую транзакцию CommittableTransaction, открывает соединение SqlConnection и получает соединение xNika из ConnectionPool, подписывает транзакцию на событие ее потока OnIdle с целью осуществления отката в случае превышения транзакцией таймаута в 1 час, инициализирует очередь транзакции, состоящую из заданий WorkItem, и классификатор используемых транзакцией файлов TransactionFile. Затем асинхронно ожидается завершение этого задания. Для выполнения заданий в транзакции созданы методы Invoke() и InvokeAsync(), принимающие на вход указатель на метод и его аргументы. Оба метода если транзакция не находится в состоянии завершения помещают задачу на выполнение: создают задание WorkItem, если в настоящий момент в очереди потока не находится ничего от имени этой транзакции, то она подписывается на события активации WorkActivating, исключения WorkException и завершения WorkComplete выполнения задания, а затем помещает это задание в очередь потока; иначе задание помещается в очередь транзакции. Если транзакция находится в состоянии завершения, поступающие от ее имени новые задания игнорируются. Разница методов состоит в том, что InvokeAsync() не ожидает завершения задания и возвращает интерфейс IAsyncResult объекта CWIAsync, отслеживающего завершение, в то время как метод Invoke() ожидает завершения задания и возвращает результат выполнения, упакованный в тип Object, кроме того, если во время выполнения произошла ошибка, метод генерирует соответствующее исключение. Текущее обрабатываемое в транзакции задание доступно через свойство транзакции CurrentWorkItem, так же как и ее соединение xNika, которое может использоваться заданием в произвольном порядке, доступно через свойство Connection. Транзакция так же имеет при себе контекст, представляющий собой словарь объектов, доступных по строковым идентификаторам. Для работы с контекстом предназначены методы AddContextObject(), добавляющий объект в контекст, и GetContextObject(), возвращающий объект по значению. Для работы с файлами в рамках транзакции предусмотрены методы CreateFile() и GetFile(), возвращающие объект TransactionFile. Метод CreateFile() принимает на вход полное имя файла, по которому создает объект TransactionFile и сохраняет его в списке используемых транзакцией файлов. Метод GetFile() возвращает из списка существующий TransactionFile по его идентификатору или null, если такового нет (описание класса - см. ниже). Для работы с менеджером блокировок BlockManager предусмотрены методы EnterLock(), TryEnterLock() и IsInLock(). Метод EnterLock() - основной метод для внесения блокировок, он принимает на вход идентификатор блокировки и создает на его основе объект CLockWorkItem, который затем добавляется в очередь потока или транзакции по тому же принципу, что и при вызове InvokeAsync(). Однако для внесения блокировки из задания данный метод не используется, а вместо него должен вызываться метод TryEnterLock(), который тоже принимает на вход идентификатор блокировки, сразу пытается ее выполнить и в случае успеха возвращает истину. При нарушении правила использования блокирующих методов генерируются соответствующие исключения. Метод IsInLock() проверяет, была ли внесена соответствующая передаваемому ему идентификатору блокировка.

Важную роль в работе всей подсистемы играет обработка классом транзакции событий WorkActivating, WorkComplete и WorkException, генерируемых объектом задания. Обработчик WorkActivating отмечает в контексте потока и в статическом поле класса Transaction текущую транзакцию, которая доступна через статическое свойство Current. Обработчик WorkComplete если завершившееся задание было типа CLockWorkItem и не закончилось успешно, добавляет в очередь потока заново его копию на выполнение; иначе в очередь рабочего потока добавляется очередное задание из очереди транзакции. Обработчик WorkException проверяет, когда произошла ошибка: если во время выполнения одного из заданий, либо на стадии фиксации, то вызывается метод Rollback() транзакции, выполняющий откат, а затем генерируется исключение; ошибка на стадии отката не обрабатывается и исключение генерируется сразу.

Для фиксации транзакции Transaction используется метод Commit(), а для отката - Rollback(). Метод Commit() выполняется только если не возникло никаких ошибок и не выполняется уже он или откат. Метод отписывает транзакцию от событий простоя ее потока, создает задание, которое обрабатывает событие OnCommit объекта транзакции, фиксирует соединение xNika и включающую транзакцию CommittableTransaction, применяет все изменения, внесенные транзакцией в файлы TransactionFile и помещает в очередь потока освобождение ресурсов, это задание помещается в очередь транзакции или рабочего потока, а затем метод Commit() ожидает его завершения. Освобождение ресурсов осуществляет функция FreeResources(): соединение xNika помечается в ConnectionPool как свободное, освобождает ресурсы, занятые соединением SqlConnection и файлами TransactionFile, удаляет все объекты из контекста, чтобы они не занимали память, выполняет обработчики события транзакции Complete и сигнализирует о завершении транзакции через ее свойство CompleteHandle. Метод Rollback() выполняется если не выполняется уже он или фиксация; метод отписывает транзакцию от событий простоя ее потока, создает задание, которое выполняет обработчики события транзакции OnRollback, выполняет откат включающей транзакции CommittableTransaction и ставит в очередь потока освобождение ресурсов, затем метод помещает это задание в очередь транзакции или рабочего потока и ожидает его завершения. Транзакция Transaction так же предоставляет возможность получения своего интервала простоя через свойство MyIdleInterval и численного уникального идентификатора через свойство ID.


Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.