17 авг. 2007 г.

Доступ к внешним системам и механизм глобальных транзакций на платформе J2EE: часть 1

Disclaimer
Цель этой статьи - дать общее представление и систематизировать знания о процессах взаимодействия с внешними ресурсами и управления глобальными транзакциями из j2ee-приложений. Статья ориентирована на начинающих j2ee-разработчиков.
Более подробно эта и другие темы раскрываются в курсах по направлениям java и j2ee, проводимых на площадке Люксофта. Презентации курсов я выкладываю по адресу http://courses.4shared.com в разделе courses. В данной статье используются материалы курса по JDBC API.

Введение

В статье речь пойдет об одном из наиболее востребованных сервисов платформы j2ee - сервисе JTA (Java Transaction API). Сервисы платформы j2ee - это уже реализованный функционал, являющийся частью j2ee-контейнера. Задача j2ee-сервисов - снять с разработчика приложений инфраструктурные задачи с тем, чтобы он концентрировался только на реализации бизнес-логики.

Процесс взаимодействия с внешними р
есурсами из j2ee-приложений
Большинство приложений так или иначе взаимодействуют с внешними системами для получения, обработки и сохранения данных. Наиболее распространенные типы внешних систем: СУБД, JMS, внешние корпоративные системы (КИС/EIS). Соответственно, для каждого из типов предусмотрен свой API доступа:
  • для СУБД - JDBC API (Java Database Connectivity)
  • для JMS - JMS API (Java Messaging Service)
  • для EIS - J2CA API (Java2 Connector Architecture), циферка 2 добавлена, чтобы не было путаницы с JCA - Java Cryptography Architecture ;-)
Взаимодействовать с внешними системами из приложения можно напрямую или с использованием соответствующего сервиса.
Взаимодействие напрямую с внешней системой подразумевает работу с ней с использованием двухзвенной схемы клиент-сервер (приложение-клиент взаимодействует с внешней системой-сервером). при этом используется соответствующий API, в клиенте инкапсулируется информация о местоположении системы и учетная запись доступа.
Но есть более удобный способ - воспользоваться j2ee-сервисом доступа. Этот сервис - промежуточное звено между клиентом и внешней системой. Что же такого полезного реализует этот пресловутый сервис?
  1. Сервис сам по себе доступен клиенту через тот же API, что и скрываемая за ним внешняя система.
  2. Сервис настраивается администратором j2ee-сервера. Такое разделение ролей админа и разработчика позволит резко повысить поддерживаемость приложения: админ может изменять расположение внешней системы и учетную запись доступа к ней без модификации приложения.
  3. Сервис обеспечивает пулирование соединений с внешней системой. Это значит, что сервис заранее открывает кучу соединений (т.н. пул соединений) с внешней системой. Теперь при использовании сервиса когда приложению понадобится соединение, то оно попросит соединение у нашего сервиса и получит уже открытое соединение, т.е. не будет тратить время на его установку.
    Почему это так важно и когда следует пользоваться сервисом в обязательном порядке: дело в том, что для stand-alone клиентов (которые на рабочих местах пользователей) время получение соединения не так критично. Тетя-бухгалтер щелкает кнопочку в своем fat-клиенте, при обработке нажатия она подождет пол-секунды и не расстроится ;-) Одна тетя - одно приложение.
    А вот если приложение работает на сервере, т.е. на него приходится множество жаждущих клиентов (много теть - один сервлет), то эти пол-секунды установки соединений выливаются в большие проблемы с нагрузочной масштабируемостью.
Резюмируя возможные комбинации вида "тип клиента - сервис доступа", можно нарисовать следующую картинку для любых технологий:

рис. 1

Небольшое терминологическое отступление: в общем случае промежуточные системы, через которые производится доступ к СУБД, называются DB middleware. Но т.к. мы говорим о платформе j2ee, то эту роль у нас будет играть jdbc-сервис.
Таким образом, предыдущая картинка с учетом специфики платформы j2ee будет выглядеть так:

рис. 2

В дальнейшем мы будем подразумевать работу с внешними системами именно через сервисы. В описаниях будет подразумеваться JDBC, если не указано иное.

Реализация сервисов доступа к внешним системам.

Как же устроены эти сервисы и как разработчику приложений работать с ними?
Технически сервис представляет из себя совокупность Resource Managers (RM) - менеджеров ресурсов. Настраивает эти менеджеры, как правило, администратор j2ee-сервера (есть еще вариант автоматической настройки при развертывании приложения, но это детали). RM представляет из себя совокупность следующих компонентов: пула соединений (connection pool) и фабрики соединений (connection factory).
Давайте рассмотрим роль и механизм настройки этих компонентов:
  1. Пул соединений.
    Задача пула - минимизировать время получения соединения с внешней системой из приложений.
    Для этого контейнер заранее открывает несколько соединений (пул) и по требования приложения отдает уже открытое соединение. Приложение не тратит время на получение соединения.
    При этом пул, отдав соединение клиенту, помечает его как используемое и не будет выдавать его другим клиентам. Когда клиент закрывает соединение, оно не закрывается физически, а всего лишь помечается как вновь свободное.

    Вопрос 1: как этого можно добиться? Как можно перехватить метод закрытия соединения, чтобы оно не закрывалось, а помечалось как свободное?
    Какие настройки необходимы? Пул выступает непосредственным клиентом для СУБД, поэтому его необходимые настройки аналогичны настройке простого клиента для СУБД: местоположение СУБД (т.н. JDBC-URL или DB-URL) и учетная запись, под которой пул аутентифицируется перед СУБД (так же есть возможность указывать учетную запись непосредственно при получении соединения из приложения).
    Так же необходимо указать используемый класс jdbc-драйвера, тем самым, по сути, определив тип используемой СУБД.
    Какие настройки достаточны?
    Помимо необходимых настроек, различные производители контейнеров или разработчики классов пулов предлагают так же возможность тонкого тюнинга пула: начальный и максимальный размер пула; таймауты на получение и удержание соединения; connection leak detection и многое другое.

    рис. 3

  2. Фабрика соединений.
    Клиент не работает с пулом напрямую. Для получения соединений он использует фабрику.

    Фабрика (Factory) - очень популярный шаблон проектирования (design pattern). Используется в тех случаях, когда необходимо инкапсулировать логику создания/получения некоторого объекта или их группы.
    Задача фабрики - скрыть сложность и обеспечить клиентам единую точку получения соединений. Классы пула и фабрики являются частью контейнера (или внешним продуктом, что реже). Поэтому администратору при создании RM достаточно только настроить пул и фабрику.
    Т.к. одной из задач фабрики является обеспечение единой точки получения соединений, возникает вопрос: где клиент найдет фабрику? Специально для этих целей j2ee-контейнер предлагает сервис именования, доступный через JNDI API.

    Сервис именования - один из предоставляемых j2ee-контейнером сервисов. Он предназначен для хранения объектов, необходимых приложениям. К этим объектам можно отнести фабрики соединений и EJB. Хранение объектов в сервисе именования организовано в виде дерева, поэтому приложению необходимо знать путь (aka jndi-name) нужного объекта в дереве. Так же сервис именования часто называют jndi-сервисом, jndi-деревом.
    Поэтому клиенту необходимо сначала получить фабрику из сервиса именования, а затем получить у неё соединение с внешним ресурсом.

    рис. 4

    Какие настройки необходимы? Администратор, настраивая фабрику, дожен указать пул, поверх которого устанавливается фабрика, и jndi-имя этой фабрики, по которому её будут искать приложения.
    Еще одна важная задача фабрики - обеспечить измененное поведение соединений.
    На самом деле фабрика выдает не native jdbc connection, класс которых настраивается в пуле, а класс-обертку. Так сделано, чтобы изолировать клиента от типа СУБД и модифицировать поведение соединений. Например, клиентский вызов метода закрытия соединения не должен приводить к закрытию, а только к пометке соединения как вновь свободного (см. Вопрос 1). Еще один пример необходимости модифицированного поведения будет рассмотрен позже при обсуждении эмуляции интерфейса XA в драйверах, его не поддерживающих.

    Приняты следующие названия фабрик соединений для различных типов ресурсов:
    • JDBC - DataSource
    • JMS - JMS Connection Factory
    • J2CA - J2CA Connection Factory
Резюме
Резюмируя, можно нарисовать следующие схемы работы приложения с RM:

рис. 5

рис. 6

Что дальше?
Во второй части статьи будут рассмотрены: механизм глобальных транзакций, интерфейс XA, JTA API.

PS Любые вопросы и предложения приветствуются ;-)

2 комментария:

batigoal комментирует...

Я правильно понимаю, что в общем случае этот самый DB Middleware географически расположен на сервере приложений?

ekr комментирует...

Как раз в общем случае DB-middleware - самостоятельный обособленный продукт.
А в конкретном случае платформы j2ee DB middleware является частью j2ee-сервера и называется JDBC-сервис ))