Перейти к тексту

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Вся введенная информация защищена.

  • Закрыть [x]

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Вся введенная информация защищена.

  • Закрыть [x]

Теория и практика Java: Понимание JTS - Волшебство за кулисами

Как контейнеры J2EE скрывают сложность управления транзакциями

Брайан Гетц, главный консультант, Quiotix
Брайан Гетц (Brian Goetz) - консультант по ПО и последние 15 лет работал профессиональными разработчиком ПО. Сейчас он является главным консультантом в фирме Quiotix, занимающейся разработкой ПО и консалтингом и находящейся в Лос-Альтос, Калифорния. Следите за публикациями Брайана в популярных промышленных изданиях. Вы можете связаться с Брайаном по адресу brian@quiotix.com

Описание:  В Части 1 данной серии, посвященной транзакциям, мы узнали, что такое транзакции, и почему они важны при построении надежного распределенного приложения. В данной части мы изучим, как приложения J2EE конструируются при помощи транзакций, и как JTS и контейнер J2EE управляют сервисами транзакций, включая демаркацию транзакций, внесение ресурсов в список, и прохождение транзакций, в режиме, практически скрытом от разработчика компонентов.

Больше статей из этой серии

Дата:  01.03.2007
Уровень сложности:  простой
Активность:  3643 просмотров
Комментарии:  


В Части 1 данной серии мы рассмотрели транзакции и изучили их основные свойства - атомарность, непротиворечивость, изолированность и долговечность. Транзакции - структурные единицы надежного приложения; было бы невероятно сложно написать надежное распределенное приложение без транзактной поддержки. К счастью, Сервис транзакций Java (JTS) и контейнер J2EE выполняют за вас большую часть работы по управлению транзакциями автоматически, поэтому от вас не потребуется познаний в области транзакций для включения этой информации непосредственно в код компонента. Результат кажется поистине волшебством - следуя нескольким простым правилам, приложение J2EE может автоматически получать транзактную семантику при небольшом дополнении в коде компонента, или вовсе без него. Данная статья имеет целью развеять мистический ореол этого волшебства, показав, как и когда происходит управление транзакциями.

Что такое JTS?

JTS - транзактный монитор компонента. Что это значит? В первой части мы ввели понятие монитора обработки транзакций (TPM), программы, координирующей выполнение распределенных транзакций для приложения. TPM существуют почти столько же времени, сколько БД; в конце 1960-х IBM был разработан CICS, использующийся и по сей день. Классические (или процедурные) TPM управляют транзакциями, будучи описаны процедурно как последовательности операций на транзактных ресурсах (таких как БД). С появлением распределенных протоколов объектов, таких как CORBA, DCOM, и RMI, возникла необходимость более объектно-ориентированного подхода к транзакциям. Обеспечение транзактной семантики для объектно-ориентированных компонентов требовало усовершенствования модели TPM, где транзакции определялись бы вызываемыми методами транзактных объектов. JTS и есть такое усовершенствование: транзактный монитор компонента (иногда также называемый объектным монитором компонента), или CTM.

На разработку JTS и транзактной поддержки J2EE оказал большое влияние Транзакционный сервис объектов CORBA (OTS). Фактически JTS реализует OTS и действует в качестве интерфейса между Java API для транзакций, API низкого уровня для определения границ транзакций, и OTS. Использование OTS вместо изобретения нового объектного транзактного протокола строится на основе существующих стандартов и создает условия для совместимости компонентов J2EE и CORBA.

На первый взгляд, переход от процедурных систем управления транзакциями к CTM кажется всего лишь сменой терминологии. Однако, разница намного серьезнее. Когда транзакция в CTM выполняется или отменяется, все изменения сделанные объектами включенными в транзакцию выполняются или отменяются в группе. Но как CTM узнает, что объекты выполнили в ходе транзакции? транзактные компоненты типа EJB не имеют методов commit() или rollback(), не регистрируют они и изменений при помощи монитора транзакций. Итак, как действия, выполняемые компонентами J2EE, становятся частью транзакции?


Невидимое внесение ресурсов в список

Хотя состояние приложения изменяется компонентами, оно все еще хранится в менеджере транзактных ресурсов (например, базы данных или серверы очередей сообщений), которые могут быть зарегистрированы как системы управления ресурсами распределенной транзакции. В первой части мы обсудили, как и почему несколько систем управления ресурсами могут быть перечислены в одной транзакции, управляемой менеджером транзакции. Системы управления ресурсами знают, как связывать изменения в состоянии приложения с конкретными транзакциями.

Но данный вывод не отвечает на наш вопрос, а просто смещает фокус с компонента на систему управления ресурсами - как контейнер определяет, какие ресурсы задействованы в транзакции, чтобы он мог внести их в список? Рассмотрим приведенный ниже фрагмент программы, который содержится в типовом сессионном компоненте EJB:


Листинг 1. Невидимое внесение ресурсов в список для управляемой компонентом транзакции
                
  InitialContext ic = new InitialContext();
  UserTransaction ut = ejbContext.getUserTransaction();
  ut.begin();

  DataSource db1 = (DataSource) ic.lookup("java:comp/env/OrdersDB");
  DataSource db2 = (DataSource) ic.lookup("java:comp/env/InventoryDB");
  Connection con1 = db1.getConnection();
  Connection con2 = db2.getConnection();
  // perform updates to OrdersDB using connection con1
  // perform updates to InventoryDB using connection con2
  ut.commit();

Обратите внимание на то, что в данном примере не содержится программного кода для внесения соединений JDBC в текущую транзакцию - контейнер сам делает это за нас. Давайте посмотрим, как это происходит.

Три типа программ управления ресурсами

Когда компоненту EJB требуется доступ к БД, серверу очереди сообщений или другому транзактному ресурсу, он устанавливает соединение в менеджером ресурсов (обычно при помощи JNDI). Более того, спецификация J2EE признает лишь три типа транзактных ресурсов - БД JDBC, серверы очереди сообщений JMS и "другие транзактные сервисы, доступ к которым осуществляется посредством JCA." Сервисы последнего класса (такие как системы ERP) должны подключаться через JCA (Архитектура коннектора J2EE). Для каждого из этих типов ресурсов необходима помощь контейнера или провайдера для включения в список ресурсов транзакции.

В Листинге 1 вы видите con1 и con2 - обычные подключения JDBC, такие как возвращаемые DriverManager.getConnection(). Мы получаем эти подключения от JDBC DataSource, который был получен по результатам поиска имени источника данных в JNDI. Имя, использованное в нашем компоненте EJB для поиска источника данных (java:comp/env/OrdersDB) уникальное для компонента; раздел resource-ref в дескрипторе размещения компонента соотносит его с именем JNDI некоторого источника DataSource, используемого приложениями и управляемого контейнером.

Скрытая программа управления JDBC

Каждый контейнер J2EE может создавать готовые к транзакции объединенные объекты DataSource, но спецификация J2EE не объясняет, как, поскольку это не является ее частью. Просмотрите документацию по J2EE и не вы не обнаружите никакой информации о том, как создавать источники данных JDBC. Чтобы получить эту информацию, Вам нужно просмотреть документацию Вашего контейнера. В зависимости от Вашего контейнера создание источника данных включает добавление описания источника данных в файл свойств или конфигурации, или может быть выполнено посредством инструментов администрирования GUI.

Каждый контейнер (или мастер объединенных подключений, такой как PoolMan) предоставляет свой собственный механизм для создания DataSource, именно в этом механизме и скрывается волшебство JTA. Мастер объединенных подключений получает Connection от определенной программы управления JDBC, но прежде, чем вернуть его приложению, заключает его в фасадный метод, который также реализует Connection, таким образом становясь между приложением и устанавливаемым соединением. Когда подключение создано или процедура JDBC выполнена, фасадный метод запрашивает менеджер транзакции, является ли текущий поток выполняемым в контексте транзакции и автоматически вносит Connection в список транзакции, если таковая существует.

Другие типы транзактных ресурсов, очереди сообщений JMS и соединители JCA, используют сходные приемы для сокрытия внесения ресурсов в список от пользователя. Когда Вы делаете доступной очередь JMS для приложения J2EE во время его размещения, Вы снова используете предоставляемый провайдером механизм для создания управляемых JMS-объектов (мастера соединений очереди и адресата подключений), которые вы затем публикуете в области имен JNDI. Управляемые объекты, созданные провайдером содержат сходный код автоматического внесения в список ресурсов, что и метод-надстройка JDBC, добавляемый поставляемым контейнером мастером объединенных подключений.


Контроль невидимых транзакций

Два типа транзакций J2EE - управляемые контейнером и управляемые компонентом - различаются в том, как они начинаются и заканчиваются. Начало и конец транзакции называются демаркацией транзакции. Пример программы, приведенный в Листинге 1 показывает управляемую компонентом транзакцию (или программная транзакция.) Управляемые компонентом транзакции начинаются и заканчиваются явным образом, компонентом, использующим класс UserTransaction (Пользовательская Транзакция). UserTransactionдоступен компонентам EJB через метод ejbContext и другим компонентам J2EE - посредством JNDI.

Управляемые контейнером транзакции (или описательные транзакции) начинаются и заканчиваются контейнером невидимо для приложения, на основании атрибутов транзакций в дескрипторе размещения компонента. Вы указываете, использует компонент транзакцию управляемую компонентом или контейнером, устанавливая значение атрибута transaction-type равным Container (Контейнер) или Bean (Компонент).

Для управляемых контейнером транзакций Вы можете устанавливать транзактные атрибуты на уровне любого класса EJB или метода; Вы можете задать набор атрибутов по умолчанию для класса EJB, а также указать атрибуты для каждого метода, если разные методы имеют разную транзактную семантику. Эти транзактные атрибуты задаются в разделе container-transaction дескриптора сборщика. Примерный дескриптор сборщика приведен в Листинге 2. Поддерживаемые значения для trans-attribute:

  • Supports (поддерживает)
  • Required (требуемый)
  • RequiresNew (требует новый)
  • Mandatory (обязательный)
  • NotSupported (не поддерживаемый)
  • Never (никогда)

Атрибут trans-attribute определяет, поддерживает ли метод выполнение внутри транзакции, какие действия должен выполнить контейнер, если метод вызван внутри транзакции, а какие - если вне ее. Самым распространенным атрибутом управляемой контейнером транзакции является Required. Когда задан атрибут Required, незавершенная транзакция внесет ваш компонент в свой список, при отсутствии выполняемой транзакции контейнер начнет транзакцию для вас. Мы рассмотрим разницу между разными типами атрибутов транзакций и изучим, когда они Вам могут пригодиться, в третьей части данной серии статей.


Листинг 2. Пример дескриптора сборщика EJB
                
<assembly-descriptor>
  ...
  <container-transaction>
    <method>
      <ejb-name>MyBean</ejb-name>
      <method-name>*</method-name>
    </method>
    <trans-attribute>Required</trans-attribute>
  </container-transaction>
  <container-transaction>
    <method>
      <ejb-name>MyBean</ejb-name>
      <method-name>updateName</method-name>
      </method>
   <trans-attribute>RequiresNew</trans-attribute>
  </container-transaction>
  ...
</assembly-descriptor>

Мощные, но опасные

В отличие от примера, приведенного в Листинге 1, с описательной демаркацией транзакций, в методах компонентов нет кода управления транзакциями. Это не только упрощает чтение конечного кода компонента (поскольку он не загроможден кодом управления транзакциями), но и имеет другое преимущество - транзактная семантика компонента может быть изменена во время сборки приложения без изменения или даже обращения к исходному коду компонента.

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


Невидимое продолжение транзакции

Для любого типа транзакций поступление ресурсов невидимо; контейнер автоматически вносит в список любые транзактные ресурсы, использованные при выполнении транзакции, для текущей транзакции. в этот процесс включены не только ресурсы, использованные транзактным методом, такие как подключения к БД, полученные в Листинге 1, но также и вызываемые методы, даже удаленные. Давайте изучим, как это происходит.

Контейнер связывает транзакции с потоками

Скажем, метод methodA() объекта A начинает транзакцию, а затем вызывает метод methodB() объекта B, которому требуется подключение JDBC, и обновляет БД. Подключение, установленное объектом B, будет автоматически внесено в транзакцию, созданную A. Как контейнер узнал, что это нужно сделать?

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

Скелеты в шкафу

Что, если объект B на самом деле подпрограмма компонента EJB, выполняемая в другом потоке или даже JVM? Удивительно, но ресурсы, используемые удаленным объектом B, все равно будут вноситься в список текущей транзакции. Подпрограмма объекта EJB (часть, выполняемая в среде вызова), протокол EJB (RMI над IIOP), и структурный объект (скелет программы) на удаленном конце все сговорились выполнить эту операцию невидимо. Подпрограмма определяет, выполняет ли вызывающий ее объект транзакцию. Если да, идентификатор (ID) транзакции, или Xid, передается удаленному объекту как часть вызова IIOP вместе в параметрами метода. (IIOP протоком удаленного вызова CORBA, предоставляющий для передачи разные элементы выполнения контекста, такие как контекст транзакции и контекст защиты; см. Ресурсы для получения дополнительной информации о RMI над IIOP.) Если вызов является частью транзакции, структурный объект на удаленной системе автоматически посылает контекст удаленного потока транзакции, чтобы когда настоящий удаленный метод будет вызван, он был бы уже частью транзакции. (Подпрограмма и структурные объекты также заботятся о начале и завершении управляемой контейнером транзакции.)

Транзакции могут быть инициированы любым компонентом J2EE - компонентом EJB, сервлетом или страницей JSP (или клиентом приложения, если контейнер поддерживает такой вариант). Это значит, что ваше приложение может начать транзакцию в сервлете или странице JSP при получении запроса, выполнить некую обработку внутри сервлета или страницы JSP, получить доступ к entity-компонентам и сессионным компонентам на нескольких сервлетах как часть логики страницы, и вся эта работа будет частью одной невидимой транзакции. На рисунке 1 показано, как транзактный контекст следует по пути выполнения от сервлета к EJB и от EJB к EJB.


Рисунок 1. Несколько компонентов в одной транзакции
Несколько компонентов в одной транзакции

Оптимизация

Управление транзакциями позволяет контейнеру вносить некоторые оптимизационные изменения. На Рисунке 1 показан сервлет и несколько EJB-компонентов, доступ к которым осуществляется как к БД внутри контекста одной транзакции. Каждое устанавливает соединение Connection с БД; вполне вероятен случай, когда каждый из них обращается к одной и той же БД. JTS может установить, используется несколько ресурсов в транзакции или нет, даже если несколько подключений установлено с одним и тем же ресурсом от разных компонентов, а затем оптимизировать выполнение транзакции. Вспомните Часть 1: вызов нескольких менеджеров ресурсов в одной транзакции требует использования протокола двухфазного контроля выполнения транзакции, что дороже использования однофазного контроля, применяемого одним менеджером ресурсов. JTS может определить, указан ли в списке транзакции лишь один менеджер ресурсов. Если будет обнаружено, что все используемые в транзакции ресурсы на самом деле - один и тот же ресурс, использование двухфазного протокола может не быть задействовано и менеджер ресурсов обработает транзакцию самостоятельно.


Заключение

Волшебство, позволяющее делать транзакции невидимыми, вносить ресурсы в список транзакции и передавать транзакцию, является не частью JTS, а частью того, как контейнеры J2EE скрытым образом используют сервисы JTA и JTS от имени приложений J2EE. Существует множество элементов, работающих вместе в скрытом режиме, чтобы эти магические действия проходили невидимо; подпрограммы EJB и структурные объекты, программы управления JDBC и надстройки, предоставляемые поставщиками контейнеров, программы управления JDBC предоставляемые поставщиками БД, поставщиками JMS и коннекторами JCA. Все они взаимодействуют с менеджером транзакции, чтобы программе вашего приложения не пришлось этим заниматься.

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


Ресурсы

Об авторе

Брайан Гетц (Brian Goetz) - консультант по ПО и последние 15 лет работал профессиональными разработчиком ПО. Сейчас он является главным консультантом в фирме Quiotix, занимающейся разработкой ПО и консалтингом и находящейся в Лос-Альтос, Калифорния. Следите за публикациями Брайана в популярных промышленных изданиях. Вы можете связаться с Брайаном по адресу brian@quiotix.com

Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Спасибо. Эта запись была помечена для модератора.


Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Сообщение о нарушении не было отправлено. Попробуйте, пожалуйста, позже.


developerWorks: вход


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Выберите ваше отображаемое имя

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

(Должно содержать от 3 до 31 символа.)


Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Оценить эту статью

Комментарии

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=199278
ArticleTitle=Теория и практика Java: Понимание JTS - Волшебство за кулисами
publish-date=03012007
author1-email=brian@quiotix.com
author1-email-cc=

Теги

Help
Используйте форму поиска, чтобы найти любой контент с данным тегом в My developerWorks. Используйте ползунок, чтобы отразить больше или меньше тегов.

КнопкаПопулярные теги отображает самые распространенные теги для данной области контента (например: Java, Linux, WebSphere).

Кнопка Мои теги отображает Ваши теги для данной области контента (например: Java, Linux, WebSphere).

Используйте форму поиска, чтобы найти любой контент с данным тегом в My developerWorks. Кнопка Популярные теги отображает самые распространенные теги для данной области контента (например: Java, Linux, WebSphere). Кнопка Мои теги отображает Ваши теги для данной области контента (например: Java, Linux, WebSphere).