Статьи

Как контролировать источник данных Java EE

Вступление

FlexyPool — это платформа с открытым исходным кодом, которая может отслеживать использование соединения с источником данных. Этот инструмент возникает из-за необходимости, так как ранее у нас не было поддержки для предоставления пулов соединений.

Изначально FlexyPool был разработан для автономных сред, а конфигурация прокси-сервера DataSource была выполнена программно. Используя псевдонимы bean-компонентов Spring , мы могли бы даже заменить уже сконфигурированный источник данных альтернативой прокси с поддержкой FlexyPool Metrics.

Поддержка Java EE

Недавно меня спросили о поддержке сред Java EE, и в истинном духе открытого исходного кода я принял вызов. Поддерживать управляемую среду сложно, потому что DataSource полностью отделен от логики приложения и доступен через поиск JNDI .

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

Хотя DataSource может и не быть регулируемым, мы можем по крайней мере отслеживать использование соединения, и этого достаточно для поддержки сред Java EE.

Добавление декларативной конфигурации

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

По умолчанию FlexyPool ищет файл flexy-pool.properties в текущем пути к классам. Расположение можно настроить с помощью системного свойства flexy.pool.properties.path , которое может быть:

  • URL (например, файл: / D: /wrk/vladmihalcea/flexy-pool/flexy-pool-core/target/test-classes/flexy-pool.properties )
  • Путь к файловой системе (например, D: \ wrk \ vladmihalcea \ flexy-pool \ flexy-pool-core \ target \ test-classes \ flexy-pool.properties )
  • Вложенный путь к пути к классам (например, nested / fp.properties )

Файл свойств может содержать следующие параметры конфигурации:

Имя параметра Описание
flexy.pool.data.source.unique.name Каждый экземпляр FlexyPool требует уникального имени, чтобы домены JMX не конфликтовали
flexy.pool.data.source.jndi.name Расположение источника данных JNDI
flexy.pool.data.source.jndi.lazy.lookup Нужно ли искать источник данных лениво (полезно, когда целевой источник данных недоступен, когда создается экземпляр FlexyPoolDataSource)
flexy.pool.data.source.class.name Источник данных может быть создан во время выполнения с использованием этого имени класса
flexy.pool.data.source.property. * Если экземпляр DataSource создается во время выполнения, каждый flexy.pool.data.source.property. $ {Java-bean-property} устанавливает свойство java-bean-свойства вновь созданного DataSource (например, flexy.pool.data.source. property.user = са)
flexy.pool.adapter.factory Указывает PoolAdaptorFactory, если DataSource поддерживает динамическое определение размера. По умолчанию используется универсальный DataSourcePoolAdapter, который не поддерживает автоматическое масштабирование.
flexy.pool.metrics.factory Определяет MetricsFactory, используемый для создания метрик
flexy.pool.metrics.reporter.log.millis Указывает интервал регистрации журнала метрик
flexy.pool.metrics.reporter.jmx.enable Указывает, следует ли включать отчеты jmx
flexy.pool.metrics.reporter.jmx.auto.start Указывает, должен ли сервис jmx запускаться автоматически (установите для этого параметра значение true в средах Java EE)
flexy.pool.strategies.factory.resolver Указывает класс ConnectionAcquiringStrategyFactoryResolver, который будет использоваться для получения списка объектов ConnectionAcquiringStrategyFactory. Это должно быть установлено, только если PoolAdaptor поддерживает доступ к размеру пула DataSource.

Hibernate ConnectionProvider

Большинство приложений Java EE уже используют JPA, и для тех, кто использует Hibernate, мы можем использовать свойство конфигурации hibernate.connection.provider_class для внедрения нашего прокси-источника данных.

Hibernate предоставляет множество встроенных точек расширения, а управление подключениями полностью настраивается. Предоставляя пользовательский ConnectionProvider, мы можем заменить исходный источник данных прокси FlexyPool.

Все, что нам нужно сделать, это добавить следующее свойство в наш файл persistence.xml :

1
2
<property name="hibernate.connection.provider_class"
          value="com.vladmihalcea.flexypool.adaptor.FlexyPoolHibernateConnectionProvider"/>

За кулисами этот поставщик настраивает FlexyPoolDataSource и использует его всякий раз, когда запрашивается новое соединение:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private FlexyPoolDataSource<DataSource> flexyPoolDataSource;
 
@Override
public void configure(Map props) {
    super.configure(props);
    LOGGER.debug(
        "Hibernate switched to using FlexyPoolDataSource
    ");
    flexyPoolDataSource = new FlexyPoolDataSource<DataSource>(
        getDataSource()
    );
}
 
@Override
public Connection getConnection() throws SQLException {
    return flexyPoolDataSource.getConnection();
}

Создание фактического источника данных во время выполнения

Если вы не используете Hibernate, вам нужно подготовить FlexyPoolDataSource до того, как EntityManagerFactory завершит загрузку:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
    xsi:schemaLocation="
 
    <persistence-unit name="persistenceUnit" transaction-type="JTA">
 
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
 
        <jta-data-source>java:global/jdbc/flexypool</jta-data-source>
 
        <properties>
            <property
                name="hibernate.hbm2ddl.auto"
                value="update"/>
 
            <property
                name="hibernate.show_sql"
                value="true"/>
 
            <property
                name="hibernate.dialect"
                value="org.hibernate.dialect.HSQLDialect"/>
 
            <property
                name="hibernate.transaction.jta.platform"
                value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
        </properties>
    </persistence-unit>
</persistence>

В то время как в рабочей среде Java EE мы используем конфигурацию DataSource для конкретного сервера приложений, для простоты я собираюсь настроить FlexyPooldataSource с помощью аннотации DataSourceDefinition :

1
2
3
4
5
@DataSourceDefinition(
    name = "java:global/jdbc/flexypool",
    className = "com.vladmihalcea.flexypool.FlexyPoolDataSource")
@Stateless
public class FlexyPoolDataSourceConfiguration {}

Теперь нам нужно передать фактические свойства DataSource во FlexyPool, и это делается через файл конфигурации flexy-pool.properties:

1
2
3
4
5
6
flexy.pool.data.source.unique.name=unique-name
flexy.pool.data.source.class.name=org.hsqldb.jdbc.JDBCDataSource
flexy.pool.data.source.property.user=sa
flexy.pool.data.source.property.password=
flexy.pool.data.source.property.url=jdbc:hsqldb:mem:test
flexy.pool.metrics.reporter.jmx.auto.start=true

Фактический источник данных будет создан FlexyPoolDataSource при запуске.

Нахождение фактического источника данных из JNDI

Если фактический источник данных уже настроен сервером приложений, мы можем дать FlexyPool команду извлечь его из JNDI. Допустим, у нас есть следующая конфигурация источника данных:

1
2
3
4
5
6
7
8
9
@DataSourceDefinition(
    name = "java:global/jdbc/default",
    className = "org.hsqldb.jdbc.JDBCDataSource",
    url = "jdbc:hsqldb:mem:test",
    initialPoolSize = 3,
    maxPoolSize = 5
)
@Stateless
public class DefaultDataSourceConfiguration {}

Для прокси JSDI DataSource нам нужно настроить FlexyPool следующим образом:

1
2
3
flexy.pool.data.source.unique.name=unique-name
flexy.pool.data.source.jndi.name=java:global/jdbc/default
flexy.pool.metrics.reporter.jmx.auto.start=true

FlexyPoolDataSource определяется вместе с фактическим источником данных:

1
2
3
4
5
@DataSourceDefinition(
    name = "java:global/jdbc/flexypool",
    className = "com.vladmihalcea.flexypool.FlexyPoolDataSource")
@Stateless
public class FlexyPoolDataSourceConfiguration {}

JPA должен будет извлечь FlexyPoolDataSource вместо фактического:

1
<jta-data-source>java:global/jdbc/flexypool</jta-data-source>

В TomEE , поскольку экземпляры DataSourceDefinitions не создаются лениво, фактический DataSource может быть недоступен в реестре JNDI при обработке определения FlexyPoolDataSource.

Для этого нам нужно поручить FlexyPool отключить поиск JNDI, пока фактически не будет запрошен DataSource:

1
flexy.pool.data.source.jndi.lazy.lookup=true

Вывод

В последний раз я использовал Java EE в 2008 году, в проекте, который использовал Java EE 1.4 с EJB 2.1. После 7 лет использования исключительно Spring я приятно удивлен опытом Java EE. Arquillian — определенно мое любимое дополнение, поскольку интеграционное тестирование имеет первостепенное значение в корпоративных приложениях. CDI прост и мощен, и я рад, что внедрение зависимостей стало стандартизированным.

Но лучший актив платформы Java EE — это само сообщество. Java EE имеет очень сильное сообщество, готовое помочь вам в случае необходимости. Я хотел бы поблагодарить Стива Миллиджа (основателя Payara и C2B2) за то, что он дал мне несколько полезных советов по проектированию интеграции FlexyPool в Java EE, Алекса Сото , Антонио Гонсалвеса , Маркуса Эйзела и всех других членов Java EE, которые у меня были очень интересные. разговоры в твиттере.