Статьи

Пул соединений XA

При работе с базами данных из Java-приложения одной из проблем, с которой сталкиваются многие, является производительность. Когда требуется много соединений, пул соединений является почти обязательным. Есть довольно много вариантов, чтобы упомянуть несколько:

Трудным путем мы усвоили одну вещь: пул соединений XA не так прост. Большинство пулов соединений не поддерживают соединения XA. Некоторые реализации в документации говорят, что они предоставляют поддержку, но либо не работают, либо есть какие-то скрытые причудливые вещи, которые нам никогда не удавалось найти. В некоторых случаях вы можете обойтись без объединения, но что, если это действительно необходимо? XA транзакции уже тяжелые. Открытие и закрытие соединений каждый раз делает их еще хуже.

Поэтому нам нужен пул соединений, который поддерживает соединения XA. К счастью, есть несколько, но я думаю, что наиболее интересным является XAPool:  http://xapool.ow2.org . Однако не слишком волнуйтесь, хотя это звучит легко, чтобы заставить его работать, это не так.

Все тесты, упомянутые в этом блоге, были выполнены с использованием Mule 3.3.2 с использованием JBoss TS в качестве менеджера транзакций для XA. В качестве базы данных мы использовали H2 и Postgresql. Для простоты этого блога мы показываем только конфигурации для базы данных H2. Мы создали очень простой поток Mule, который запускает транзакцию в очереди виртуальных машин Mule и продолжается через JDBC.

Сначала давайте рассмотрим пул соединений Tomcat JDBC. В документации мы можем обнаружить, что эта библиотека обеспечивает поддержку транзакций XA. Однако нам так и не удалось заставить его работать. Мы перепробовали много разных конфигураций, но конечный результат всегда был один и тот же, он на самом деле не работал. Если кто-то может предложить решение, пожалуйста.

Попробуйте номер один, установите внутренний источник данных с помощью источника данных jdbcx H2 и настройте Tomcat Pool XADataSource. Обратите внимание, что в пуле мы ссылаемся на источник данных, это необходимо для создания соединений XA:

<spring:bean id="internalDataSource" class="org.h2.jdbcx.JdbcDataSource" >
  	<spring:property name="URL" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" />
	<spring:property name="user" value="" />
	<spring:property name="password" value="" />
</spring:bean>
		
<spring:bean id="pooledDataSource" class="org.apache.tomcat.jdbc.pool.XADataSource" >
	<spring:property name="dataSource" ref="internalDataSource" />
</spring:bean>

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

Root Exception stack trace:
org.h2.jdbc.JdbcSQLException: The object is already closed [90007-172]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
	at org.h2.message.DbException.get(DbException.java:169)
	at org.h2.message.DbException.get(DbException.java:146)

Чтобы попытаться это исправить, мы попытались обернуть источник данных H2 в XAPool StandardXADatasource на тот случай, если для XA не было правильно создано соединение. Это конфигурация:

<spring:bean id="internalDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
	<spring:property name="driverName" value="org.h2.Driver" />
	<spring:property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" />
	<spring:property name="user" value="" />
	<spring:property name="password" value="" />
</spring:bean>
		
<spring:bean id="pooledDataSource" class="org.apache.tomcat.jdbc.pool.XADataSource" >
	<spring:property name="dataSource" ref="internalDataSource" />
</spring:bean>

Это никак не помогло, возникла та же проблема, только немного другое исключение:

java.sql.SQLException: Connection is closed
	at org.enhydra.jdbc.standard.StandardConnectionHandle.preInvoke(StandardConnectionHandle.java:117)
	at org.enhydra.jdbc.core.CoreConnection.getAutoCommit(CoreConnection.java:104)
	at org.mule.transport.jdbc.xa.ConnectionWrapper.getAutoCommit(ConnectionWrapper.java:113)

Важно отметить, что обе конфигурации работают, если мы вычтем пул JDBC Tomcat. Ясно, что этот пул на самом деле не работает для транзакций XA, по крайней мере с Mule.

Последний вариант — попробовать напрямую с XAPool. XAPool предоставляет класс StandardXAPoolDataSource, поэтому это выглядело довольно многообещающе. На их сайте у них даже есть пример: здесь . Подожди, извини. Несмотря на то, что пример называется StandardXAPoolDataSource, он фактически использует StandardXADataSource без объединения в пул! Приятно!! (Я совершенно саркастичен на тот случай, если вы не заметили.)

Итак, давайте углубимся и попробуем настроить StandardXAPoolDataSource:

<spring:bean id="internalDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
	<spring:property name="driverName" value="org.h2.Driver" />
	<spring:property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" />
	<spring:property name="user" value="" />
	<spring:property name="password" value="" />
</spring:bean>

<spring:bean id="pooledDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
	<spring:property name="user" value="" />
	<spring:property name="password" value="" />
	<spring:property name="dataSource" ref="internalDataSource" />
</spring:bean>

Это выглядит многообещающе. Однако здесь есть огромная проблема. StandardXAPoolDataSource не реализует javax.sql.XADataSource. Он реализует только javax.sql.DataSource. Это означает, что когда Mule пытается получить транзакцию XA, он не справится и выдает следующее исключение:

org.mule.api.transaction.TransactionException: Endpoint is transactional but transaction does not support it
	at org.mule.transport.AbstractConnector.getTransactionalResource(AbstractConnector.java:2015)
	at org.mule.transport.jdbc.JdbcMessageDispatcher.doSend(JdbcMessageDispatcher.java:71)
	at org.mule.transport.AbstractMessageDispatcher.process(AbstractMessageDispatcher.java:81)

При внимательном рассмотрении источника, кажется, что все на месте. Только отсутствует реализация javax.sql.XADataSource и некоторые его методы. Но они нигде не появляются в библиотеке XAPool.

Установив это, мы реализовали класс, который расширяет StandardXAPoolDataSource и реализует javax.sql.XADataSource. Реализация метода getXaConnection () — это просто вызов метода getConnection () суперкласса, который выполняет всю работу по созданию пула.

package com.ricston.jdbc.xapool;

import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

import javax.sql.XAConnection;
import javax.sql.XADataSource;

import org.enhydra.jdbc.pool.StandardXAPoolDataSource;
import org.enhydra.jdbc.standard.StandardXAConnectionHandle;


public class RicstonStandardXAPoolDataSource extends StandardXAPoolDataSource implements XADataSource {

	/**
	 * 
	 */
	private static final long serialVersionUID = -6060990263159819182L;


	@Override
	public XAConnection getXAConnection() throws SQLException {
		return ((StandardXAConnectionHandle) this.getConnection()).xacon;
	}

	@Override
	public XAConnection getXAConnection(String user, String password)
			throws SQLException {
		return ((StandardXAConnectionHandle) this.getConnection(user, password)).xacon;
	}
	
	public Logger getParentLogger() throws SQLFeatureNotSupportedException{
		throw new SQLFeatureNotSupportedException();
	}

}

Теперь нам нужно только сконфигурировать его в Spring и подключить его к разъему JDBC нашего Mule, и все готово.

<spring:bean id="internalDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
	<spring:property name="driverName" value="org.h2.Driver" />
	<spring:property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" />
	<spring:property name="user" value="" />
	<spring:property name="password" value="" />
</spring:bean>

<spring:bean id="pooledDataSource" class="com.ricston.jdbc.xapool.RicstonStandardXAPoolDataSource" destroy-method="shutdown">
	<spring:property name="user" value="" />
	<spring:property name="password" value="" />
	<spring:property name="dataSource" ref="internalDataSource" />
</spring:bean>

В следующем посте мы собираемся более подробно рассмотреть, как настроить размер пула и другие интересные атрибуты.

Наслаждаться.