Если вы планируете разрабатывать системы с большим объемом данных, использующие архитектуру Java, вы, вероятно, будете использовать транзакции Jakarta (EE)/Java. По сути, они являются источником жизненной силы сложных корпоративных приложений с открытым исходным кодом. Как таковые, они требуют тщательного планирования и координации. Без планирования транзакции могут попытаться зафиксироваться на одном и том же объекте ресурсов, вызывая XAException или SystemException.
Вам необходимо добавить правильные элементы управления и инструменты, которые помогут вам отлаживать и отслеживать все ошибки, связанные с транзакциями. В этой статье показано, как находить и устранять неполадки в сбойных транзакциях. Поскольку предотвращение лучше, чем исправление, здесь также будут показаны лучшие методы кодирования и проектирования, позволяющие минимизировать и легко обрабатывать сбойные транзакции.
Что такое Java-транзакции
Транзакции (при проектировании и разработке программного обеспечения) – это, по сути, действия, выполняемые с источниками данных. Если вы работали с пакетами Java SE SQL, вы, скорее всего, сталкивались с транзакциями. Они позволяют выполнять условное выполнение нескольких инструкций. Транзакции в пакетах Java SE SQL, известные как локальные транзакции, выполняются отдельно и непосредственно на ресурсе. Таким образом, диспетчер транзакций не требуется, и они могут обрабатываться только с помощью пакета Java SQL.
Однако API транзакций Jakarta обычно обрабатывает распределенные транзакции для высокоуровневых систем и приложений на базе Java. Поскольку Oracle повторно приобрела Java Transaction API (JTA) name, пакет JTA javax также может управлять транзакциями. Эти две реализации похожи.
JTA и Jakarta Transaction API предоставляют интерфейсы и классы для управления транзакциями для (мультитранзакционных) приложений и серверов приложений. Они созданы по образцу стандарта X/Open XA, поэтому менеджер транзакций координирует выполнение транзакций с менеджером ресурсов для набора программных ресурсов.
Следовательно, в дополнение к интерфейсу демаркации транзакций приложения и интерфейсу диспетчера транзакций (для приложений), Jakarta Transaction API имеет третий уровень для стандартного сопоставления Java протокола X/Open XA.
JTA немного проще, но функционирует по тому же принципу. Он предлагает два интерфейса: один реализует транзакции (javax.transaction), а другой реализует объекты ресурсов (javax.transaction.xa).
Транзакции обычно выполняются на программных ресурсах, таких как базы данных, службы обмена сообщениями и корпоративные компоненты. У каждого из этих ресурсов есть уникальные, специфические менеджеры. Соответственно, API транзакций Jakarta имеет общие отношения со многими (если не со всеми) API Java, которые используются для абстрагирования источников данных и управления ими.
Например, драйверы Java Database Connectivity (JDBC) для систем распределенных транзакций используют интерфейс avax.transaction.xa.XAResource, javax.sql.XAConnection или javax.sql.XADataSource. Это верно как для JTA, так и для Jakarta Transactions API. Тем не менее, вы можете найти Jakarta Transactions API как часть библиотеки Jakarta EE или как отдельный модуль. В следующем разделе мы подробнее рассмотрим, как все эти разрозненные программные компоненты сочетаются друг с другом, рассмотрев, как работает система распределенных транзакций. Это должно применяться к обоим API.
Пример процесса распределенной транзакции
В случаях, когда имеется несколько распределенных реляционных баз данных, компоненты, участвующие в процессе распределенной транзакции, включают следующее:
- Приложение
- Сервер приложений
- Адаптер ресурсов (JDBC DriverManager)
- Диспетчер ресурсов (драйвер JDBC)
- Менеджер транзакций
- Базы данных
В этом примере транзакции будут поставлены в очередь и направлены в необходимые системы управления базами данных для получения соответствующей информации. Адаптер ресурсов и диспетчер преобразуют запросы в базы данных.
Сервер приложений обрабатывает основную часть запросов приложений, отправляемых приложением. Диспетчер транзакций администрирует и контролирует границы каждой транзакции и отвечает за принятие решения о том, следует ли фиксировать транзакцию или откатывать:
На предыдущей диаграмме диспетчер транзакций успешно фиксирует все транзакции. Однако это не всегда так. Иногда транзакции завершаются сбоем, и когда это происходит, диспетчер транзакций выполняет откат.
Более того, даже если диспетчер транзакций успешно завершит транзакцию, ошибка все равно может привести к сбою в адаптере ресурсов или диспетчере. Таким образом, приложение должно выполнить проверку, чтобы убедиться, есть ли какие-либо ошибки, связанные с подключением к resource manager. В некоторых случаях resource Manager может выдавать исключение.
Любые возникающие ошибки часто устраняются приложением, вызывающим метод отката диспетчера ресурсов. Но почему транзакции в первую очередь терпят неудачу, и можно ли этого избежать?
Почему Java-транзакции завершаются с ошибкой
Подобно транзакциям базы данных, транзакции Java/Jakarta следуют принципу ACID. Следовательно, они либо выполняются полностью, либо не выполняются вообще. Успешное выполнение транзакции приводит к фиксации, в то время как неудачное – к откату. Когда происходит откат, все компоненты, затронутые попыткой фиксации, возвращаются в свои состояния до фиксации.
Хотя sql пакет обрабатывает откаты автоматически, важно явно обрабатывать откаты транзакций в коде. Предпочтительно, rollback(). Предпочтительно, чтобы при обработке исключения (в блоке) вызывался метод Transaction/rollbackcatch.
Транзакции могут завершаться с ошибкой из-за неверно сформированных инструкций (если вы имеете дело с драйверами JDBC) или внезапной недоступности ресурсов. Таким образом, сбой может произойти из-за ошибки в самой транзакции или во время вызова commit. Когда происходит последнее, среда выполнения выдает TransactionRolledBackException.
Часто причиной сбоя транзакции является ошибка в области видимости или состоянии. Например, члены класса, которые должны быть общедоступными, ошибочно определяются как частные, или объекты, которые должны быть определены как статические, делаются нестатическими.
Существует множество опций и инструментов, которые вы можете использовать для определения причины сбоя транзакции.
Как устранить сбои транзакций
Примеры, используемые в этом разделе, будут посвящены пакету JTA Java SE. Однако многие концепции можно перенести на реализацию Jakarta.
[TIMESTAMP] 00000157 EmbeddableTra I WTRN0041I: Transaction 00000172E552D88A0000000115200494443873BE8709685AA43D3ED6C2AFF879F9EA3D4900000172E552D88A0000000115200494443873BE8709685AA43D3ED6C2AFF879F9EA3D4900000001 has been rolled back. [TIMESTAMP] 00000157 WSRdbXaResour E DSRA0304E: XAException occurred. XAException contents and details are: The XA Error is : -7 The XA Error message is : Resource manager is unavailable. The Oracle Error code is : 17008 The Oracle Error message is: Internal XA Error The cause is : java.sql.SQLRecoverableException: Closed Connection. [TIMESTAMP] 00000157 WSRdbXaResour E DSRA0302E: XAException occurred. Error code is: XAER_RMFAIL (-7). Exception is: XAErr (-7): Resource manager is unavailable. ORA-17008 SQLErr (0) [TIMESTAMP] 00000157 XATransaction E J2CA0027E: An exception occurred while invoking end on an XA Resource Adapter from DataSource jdbc/DraftDev, within transaction ID {XidImpl: formatId(53445741), gtrid_length(35), bqual_length(2), data(00000172e552d88a0000000115200494443873be8709685aa43d3ed6c2aff879f9ea3d4900000172e552d88a015200494443873be8709685aa43d3ed6c2aff879f9ea3d49000000010000000000000000000000000001)} : oracle.jdbc.xa.OracleXAException: XAErr (-7): Resource manager is unavailable. ORA-17008 SQLErr (0) at oracle.jdbc.xa.OracleXAResource.checkError(OracleXAResource.java:1112)
Вышеупомянутую трассировку sack может быть немного сложно расшифровать. Однако, сосредоточившись на читаемых частях, вы сможете лучше понять причину исключения. Трассировку стека можно упростить до следующих компонентов:
java.sql.SQLRecoverableException: Closed Connection An exception occurred while invoking end on an XA Resource Adapter from DataSource jdbc/DraftDev, within transaction ID {XidImpl: formatId(53445741), gtrid_length(35), bqual_length(2)
Это означает, что транзакция завершилась с ошибкой из-за недоступности конечной точки базы данных SQL. У вас есть имя источника данных, о котором идет речь: jdbc/DraftDev.
Поскольку трассировка стека исключений не указывает на какие-либо проблемы в пользовательских классах, можно сделать вывод, что сбой не был вызван проблемой в самом коде – по крайней мере, напрямую. Таким образом, исключение, должно быть, было вызвано проблемой на стороне сервера, конечной точки или подключения.
Следующим шагом будет определение источника проблемы. Однако трассировка стека обеспечивает только отправную точку, и требуется дальнейшее расследование. Рекомендуется проверить свой сервер, сетевое подключение и базу данных, чтобы убедиться в отсутствии каких-либо физических или цифровых помех, вызывающих сбой ваших транзакций.
Несомненно, этот процесс может занять много времени. Это конкретное исключение произошло из-за обновления системы безопасности на сервере. Программное обеспечение безопасности сочло подключения к базе данных подозрительными и, таким образом, заблокировало их. Основываясь только на предыдущей трассировке стека, определить эту причину довольно сложно и потребует большого количества проб и ошибок.
Транзакции с большей вероятностью завершатся сбоем из-за внезапной недоступности ресурсов. Когда приложение, которое когда-то работало (идеально), начинает выдавать исключения (ошибки), это редко связано с ошибкой в коде.
Несмотря на то, что проблема не связана строго с кодом, вы все равно можете включить в свой код непредвиденные обстоятельства для устранения подобных обстоятельств. В любом случае, отладка кода – это только часть решения.
Если вы используете базовый текстовый редактор и виртуальную машину Java (JVM) для отладки своего кода, вам придется полагаться на ведение журнала или использовать Java Debugger (jdb) интерфейс командной строки. Конечно, это не идеально для больших программ.
В качестве альтернативы вы можете использовать IDE, такую как Apache NetBeans, OpenBeans или Eclipse IDE для разработчиков Java. Они предоставляют инструменты визуальной отладки, которые позволяют устанавливать точки останова, извлекать дополнительную информацию об исключениях, выполнять ввод и вывод кода и так далее. В NetBeans также есть монитор системных ресурсов, который позволяет принудительно выполнять сборку мусора, что может быть особенно полезно при создании и запуске высоконагруженных приложений. Если ваши транзакции не были должным образом зафиксированы или откатаны, это может привести к утечке системных ресурсов при большой нагрузке.
Хотя это отличная функция, в некоторых случаях вам может потребоваться добавить JVM и инструмент профилирования приложений для надлежащего мониторинга ресурсов и точной настройки вашего приложения. Некоторые примеры таких инструментов включают следующее:
Лучшие методы программирования для предотвращения сбоев транзакций и обработки их
При попытке выполнить и зафиксировать транзакцию вы должны убедиться, что вы включили необходимую обработку исключений, которая откатит транзакцию:
} catch (javax.transaction.xa.XAException xae) { // Распределенная транзакция завершилась неудачей, поэтому откатите ее назад. // Транзакция может завершиться неудачей либо при подготовке, либо при фиксации. System.out.println("Distributed transaction prepare/commit failed. " + "Rolling it back."); System.out.println("XAException error code = " + xae.errorCode); System.out.println("XAException message = " + xae.getMessage()); xae.printStackTrace(); try { xaRes1.rollback(xid1); //Выполняет откат распределенной транзакции в первой ветви } catch (javax.transaction.xa.XAException xae1) { //Выявляет сбой в процессе отката и сообщает о нем System.out.println("distributed Transaction rollback xares1 failed"); System.out.println("XAException error code = " + xae1.errorCode); System.out.println("XAException message = " + xae1.getMessage()); }
Первый перехват запускается исключением из сбойной фиксации. Он регистрирует и печатает всю необходимую информацию, которая поможет вам найти причину исключения, а затем пытается использовать метод отката. Поскольку это может вызвать исключение, это реализовано с помощью инструкции try . Опять же, вы можете добавить больше перехватов для исключений ресурсов. Всегда перехватывайте наиболее явные исключения перед общими.
Для минимизации конфликтов каждая транзакция должна иметь глобальный идентификатор транзакции и квалификатор ветви, который должен быть уникальным для текущего диспетчера транзакций:
byte[] gtrid = new byte[] { 0x44, 0x11, 0x55, 0x66 }; //Глобальный идентификатор транзакции byte[] bqual = new byte[] { 0x00, 0x22, 0x00 }; //Определитель ветви
Система обычно отменяет или блокирует вашу транзакцию в любой момент. Таким образом, вы должны отслеживать длительные транзакции. Например, вы можете использовать операции MBean, такие как enableJDBCTiming и disableJDBCTiming, для включения и отключения синхронизации транзакций JDBC, что позволяет устанавливать тайм-ауты для транзакций. Вы также можете использовать потоки для запуска параллельных транзакций. Вы можете получить состояние потоков с помощью getThread() метода.
Кроме того, всегда помните о следующих основных рекомендациях по программированию:
- Делайте свой код как можно более кратким. Не добавляйте ненужные функции. Придерживайтесь принципа “вам это не понадобится” (YAGNI).
- При необходимости используйте распределенные транзакции. Если существует более чистый и эффективный способ выполнения основной функции вашего приложения без транзакций, используйте его.
- Не перехватывайте и не перенаправляйте исключения.
Заключение
Работа с транзакциями Java может быть кошмаром. Для системных дизайнеров и архитекторов может потребоваться большой набор разрозненных элементов. Это особенно верно, когда вы вводите в уравнение многопоточность. В этой статье рассказывается о том, что такое транзакции, почему они могут завершаться сбоем и как их можно отладить и исправить. Чаще всего транзакции завершаются сбоем из-за системных проблем. Вы можете предпринять необходимые шаги для обработки этих ситуаций в своем коде; однако полностью устранить их невозможно.
Таким образом, у вас должен быть инструмент, который упрощает отслеживание первопричины сбойных транзакций.