如何在取消部署/重新部署时关闭connectionprovider的连接?

dauxcl2d  于 2021-10-10  发布在  Java
关注(0)|答案(0)|浏览(212)

在我们正在开发的webapp中,我们有多个数据库(租户)。这些租户通过hibernate/hikari连接到他们自己的数据库 ConnectionProvider . 它们是在一个自定义类中创建的 AbstractMultiTenantConnectionProvider .
我们使用的是:spring 4.3.8、hibernate 5.0.12、hikaricp 4.0.3、tomcat 8.5/9、mysql 8。配置完全在java上完成(除了日志配置之外没有xml)。我们在一台服务器上有一个应用程序(gcp上的tomcat 9),在另一台服务器上有两个应用程序(自托管服务器上的tomcat 8.5)。gcp上的应用程序和另一个应用程序使用多租户。我也用对了 ConnectionProvider hikaricp的github wiki中所述的驱动程序。
正如标题所示,问题在于在我们重新部署或取消部署.war文件时,连接没有关闭,这会导致连接堆积并占用资源(我们曾经出现过“连接太多”错误)。
我已经对此进行了几周的调查,可以肯定地说
没有手动打开的连接。因此,代码中不存在左开连接。
这可以通过关闭服务器、更新war并再次启动服务器来解决,但这意味着我们的一项服务不必要地停机。要改变这一点,需要对服务器进行某种程度的大修。这是我最后的办法。
降低数据库中与空闲时间相关的配置没有帮助。降低连接池中的maxlifetime也无济于事。
我从 ConnectionProviderDataSources 也一样,但这并没有真正起作用。事实上,这两种情况最终都是一样的,同样的行为和一些不一致之处,比如在启动时只打开一个租户池,然后在实际使用租户时创建一个新的租户池。
唯一正确关闭的租户是“主”数据库中的租户,该数据库存储用户凭据等。这是作为bean注册的,与connectionprovider实现相比,它是唯一不同的东西。
我所尝试的:
建立习俗 stop() 方法,该方法迭代并关闭所有连接。。。但后来我发现 stop() 方法 HikariCPConnectionProvider 是一种通知方法。日志随后抛出“no-this-method”,对该方法进行了异常调用。
建立习俗 LocalContainerEntityManagerFactoryBean 用一个 @PreDestoy 方法手动关闭连接(与上面的结果相同,但对更改不起作用) DataSource 要么)。
登记每 ConnectionProvider / DataSource 为租户手动设置为bean。这也没用。
尝试在以下位置手动关闭连接: contextDestroyed() 正如这里和这里所建议的。
我有一种感觉,改变到 DataSource 这是一条路要走,但重新部署时有些不稳定的行为比堆叠连接更可怕。
我拒绝相信没有一个合适的方式来结束 ConnectionProvider 但是经过几周不停地阅读这篇文章,并没有真正找到一个方法来做这件事,我试着寻求帮助。
接下来,我认为最相关的代码是当前正在使用的代码(我仍然有一些以前的尝试) DataSource 和bean注册,如果这些值得一看的话)。
EntityManager工厂:

@Configuration
@ComponentScan
@EnableJpaRepositories
public class EmpresaConfiguracion {
    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean empresaEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setPackagesToScan("mx.com.we.tenant");
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
        properties.put("hibernate.multi_tenant_connection_provider",multiTenantConnectionProviderImpl);
        properties.put("hibernate.tenant_identifier_resolver",currentTenantIdentifierResolverImpl);
        properties.put("hibernate.multiTenancy", "DATABASE");
        em.setJpaPropertyMap(properties);
        return em;
    }
}

多租户连接提供程序:

@Service
public class MultiTenantConnectionProviderImpl extends AbstractMultiTenantConnectionProvider {
    /**
     * 
     */

    private static final long serialVersionUID = 1L;
    private Map<String, ConnectionProvider> connectionProviderMap = new HashMap<>();

    public MultiTenantConnectionProviderImpl() throws IOException, ClassNotFoundException {
        TenanIds conexion = new TenanIds();
        for (int i = 0; i < conexion.getTenants().size(); i++) {
            initConnectionProviderForTenant(conexion.getTenants().get(i));
        }
    }

    private void initConnectionProviderForTenant(String tenantId) throws IOException {
        Properties properties = new Properties();
        properties.put("hibernate.hikari.jdbcUrl",
                "jdbc:mysql://localhost:3306/".concat(tenantId).concat(Credentials.CONNECTIONURLPARAMS.toString()));
        properties.put("hibernate.hikari.dataSource.user", Credentials.USER.toString());
        properties.put("hibernate.hikari.dataSource.password", Credentials.PASS.toString());
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
        properties.put("hibernate.connection.provider_class", "org.hibernate.hikaricp.internal.HikariCPConnectionProvider");
        properties.put("hibernate.hikari.maximumPoolSize", "20");
        properties.put("hibernate.hikari.minimumIdle", "4");
        properties.put("hibernate.hikari.maxLifetime", String.valueOf(Duration.ofMinutes(15).toMillis()));
        properties.put("hibernate.hikari.poolName", tenantId.toUpperCase().concat("_POOL"));

        HikariCPConnectionProvider connectionProvider = new HikariCPConnectionProvider();
        connectionProvider.configure(properties);
        this.connectionProviderMap.put(tenantId, connectionProvider);
    }

}

tenanids类在主数据库中检查一个“clients”表,其目的是返回一个包含每个租户标识符的字符串列表。
如果您需要查看任何其他类或方法,请请求它。如果有任何写得不好的代码,也请指出。作为一名开发人员,我正在努力提高我的整体素质。
我还在下面的serverfault问题中做了一个测试,详细记录了每个想法。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题