Мультитенант Springboot с MultiTenantConnectionProvider всегда выбрасывает org.apache.tomcat.jdbc.pool.PoolExhaustedException: - PullRequest
0 голосов
/ 20 января 2019

Я начал преобразовывать свое выходящее приложение Spring Boot (1.5.4.RELEASE) для работы с мультитенантными функциями. Это мультитенантное решение на основе схемы, основанное на mysql.Как показано в документе гибернации, предложенном ниже

https://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch16.html

, я реализовал интерфейсы MultiTenantConnectionProvider и CurrentTenantIdentifierResolver, и он отлично работает.

package com.ifi.aws.tenant.config.hibernate;

import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ifi.aws.tenant.entity.TenantContext;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

@Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {

  private static final long serialVersionUID = 6246085840652870138L;

  @Autowired
  private DataSource dataSource;

@Override
public Connection getAnyConnection() throws SQLException {
  return dataSource.getConnection();
}

@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
  connection.close();
}

@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
  final Connection connection = getAnyConnection();
  try {
    connection.createStatement().execute( "USE " + tenantIdentifier );
  }
  catch ( SQLException e ) {
    throw new HibernateException(
        "Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
      e
      );
  }
  return connection;
}

@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
  try {
    connection.createStatement().execute( "USE " + TenantContext.DEFAULT_TENANT );
  }
  catch ( SQLException e ) {
    throw new HibernateException(
        "Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
      e
      );
  }
  connection.close();
}

@SuppressWarnings("rawtypes")
@Override
public boolean isUnwrappableAs(Class unwrapType) {
  return false;
}

@Override
public <T> T unwrap(Class<T> unwrapType) {
  return null;
}

@Override
public boolean supportsAggressiveRelease() {
  return true;
}

}

package com.ifi.aws.tenant.config.hibernate;

import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.springframework.context.annotation.Configuration;

import com.ifi.aws.tenant.entity.TenantContext;

@Configuration
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {

  @Override
  public String resolveCurrentTenantIdentifier() {


     String tenantId = TenantContext.getTenantSchema();

    //System.out.println("------------------ resolveCurrentTenantIdentifier = " + tenantId);
    if (tenantId != null) {
        return tenantId;
    }
    return TenantContext.DEFAULT_TENANT;
}
@Override
public boolean validateExistingCurrentSessions() {
    return true;
}
}

, а затемниже приведена моя конфигурация гибернации

package com.ifi.aws.tenant.config.hibernate;

import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cfg.Environment;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import 
org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

@Configuration
public class HibernateConfig {

  @Autowired
  private JpaProperties jpaProperties;

  @Bean
  public JpaVendorAdapter jpaVendorAdapter() {
    return new HibernateJpaVendorAdapter();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
  MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
  CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
    Map<String, Object> properties = new HashMap<>();
    properties.putAll(jpaProperties.getHibernateProperties(dataSource));
    properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
    properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
    properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);

    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource);
    em.setPackagesToScan("com.ifi.aws");
    em.setJpaVendorAdapter(jpaVendorAdapter());
    em.setJpaPropertyMap(properties);
   return em;
  }
}

, однако время от времени происходит сбой системы с приведенной ниже ошибкой

Springboot Multi-tenant with MultiTenantConnectionProvider always throw org.apache.tomcat.jdbc.pool.PoolExhaustedException: [http-nio-8086-exec-2] Timeout: Pool empty. Unable to fetch a connection in 30 seconds, none available[size:100; busy:100; idle:0; lastwait:30000].

i Прочитал на этом сайте и обнаружил точно такую ​​же проблему в следующих вопросах.

Spring Boot: пул дерби в Apache пуст.Невозможно получить соединение за 30 секунд Превышен пул соединений Tomcat

Одним из предложенных исправлений было добавление следующих конфигураций

spring.datasource.tomcat.max-active=100
spring.datasource.tomcat.max-idle=8
spring.datasource.tomcat.min-idle=8

Но все жея получаю ту же ошибку, и я отлаживаю код и обнаружил, что он закрывает соединение после каждого выполнения вызова базы данных.Ребята, у вас есть идеи?

Редактировать

Вчера я обнаружил, что API вообще не закрывает никаких соединений.Я написал простую утилиту для проверки состояния соединения, как показано ниже:

    @Autowired
private DataSource ds;

@Before("execution(* com.ifi.aws.*.dao.impl.springData.*.*(..))")
public void logBeforeConnection(JoinPoint jp) throws Throwable {
    logDataSourceInfos("Before", jp);
}

@After("execution(* com.ifi.aws.*.dao.impl.springData.*.*(..)) ")
public void logAfterConnection(JoinPoint jp) throws Throwable {
    logDataSourceInfos("After", jp);
}

public void logDataSourceInfos(final String time, final JoinPoint jp) {
    final String method = String.format("%s:%s", jp.getTarget().getClass().getName(), jp.getSignature().getName());
    logger.debug("--------------------------------------------------------------------------");
    logger.debug(String.format("%s %s: number of connections in use by the application (active): %d.", time, method, ds.getNumActive()));
    logger.debug(String.format("%s %s: the number of established but idle connections: %d.", time, method, ds.getNumIdle()));
    logger.debug(String.format("%s %s: number of threads waiting for a connection: %d.", time, method, ds.getWaitCount()));
}

}

Это показывает непрерывный рост активных соединений.

Before com.sun.proxy.$Proxy127:findOne: number of connections in use by the application (active): 21.
Before com.sun.proxy.$Proxy127:findOne: the number of established but idle connections: 0.
Before com.sun.proxy.$Proxy127:findOne: number of threads waiting for a connection: 0

-----------------

After com.sun.proxy.$Proxy127:findOne: number of connections in use by the application (active): 21.
After com.sun.proxy.$Proxy127:findOne: the number of established but idle connections: 0.
After com.sun.proxy.$Proxy127:findOne: number of threads waiting for a connection: 0.
o.h.e.t.i.TransactionImpl                : committing

-------------------

Before com.sun.proxy.$Proxy127:findOne: number of connections in use by the application (active): 21.
Before com.sun.proxy.$Proxy127:findOne: the number of established but idle connections: 0.
Before com.sun.proxy.$Proxy127:findOne: number of threads waiting for a connection: 0

-----------------

After com.sun.proxy.$Proxy127:findOne: number of connections in use by the application (active): 22.
After com.sun.proxy.$Proxy127:findOne: the number of established but idle connections: 0.
After com.sun.proxy.$Proxy127:findOne: number of threads waiting for a connection: 0.
o.h.e.t.i.TransactionImpl                : committing

-------------------

Однакоэто прекрасно в моей локальной среде, и он правильно закрывает соединения.Моя среда тестирования, развернутая в экземпляре Windows AWS t2, этот API развернут в виде файла JAR Spring Boot с сервером MYSQL, установленным в том же экземпляре t2.Единственное отличие, которое я вижу, - это версия системы OPeration, и это могут быть некоторые конфигурации сервера MYSQL

Редактировать

Мне удалось решить проблему, следуя инструкциям @xerx593

проблема была с supportAggressiveRelease = true, и я изменил его как ложное, как предложено @ xerx593.Однако мне все еще интересно, как это работает в моей локальной среде, а не в среде тестирования.Согласно документу hibernate, в нем говорится: «Поддерживает ли этот провайдер соединений агрессивное освобождение соединений JDBC и повторное приобретение этих соединений (при необходимости) позже?».И тестовая, и локальная среды имеют одинаковые конфигурации и могут быть результатом версии операционной системы или конфигурации mysql?

Спасибо, Kelum

1 Ответ

0 голосов
/ 12 февраля 2019

С помощью "грубой силы" мы обнаружили, что проблема заключалась во флаге supportsAggressiveRelease, который при установке (= true) не обнаруживал проблем в среде DEV, но приводил к проблемам в ваших экземплярах AWS.

Решение:

@Override
public boolean supportsAggressiveRelease() {
   return false;//!
}

Почему эта среда не поддерживает «агрессивный выпуск», находится в конфигурации / природе вашей среды ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...