【问题标题】:Connection closed error after some idle time一些空闲时间后连接关闭错误
【发布时间】:2018-06-02 02:59:25
【问题描述】:

我有一个多租户应用程序,其中每个租户配置一个数据库和一个主数据库。我在应用程序中加载所有数据源,如下所示:

@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource() {

    if(LOGGER.isInfoEnabled())
        LOGGER.info("Loading datasources ...");

    DataSource ds = null;
    JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

    // load MASTER datasource
    ds = dataSourceLookup.getDataSource(properties.getJndiName());

    // load other TENANTs DB details
    JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
    List<GroupConfig> groupConfigs = jdbcTemplate.query(
            "select * from master.tblTenant where IsActive=1 and ConfigCode in ('DB_URL','DATASOURCE_CLASS','USER_NAME','DB_PASSWORD') order by 2",
            new ResultSetExtractor<List<GroupConfig>>() {

                public List<GroupConfig> extractData(ResultSet rs) throws SQLException, DataAccessException {
                    List<GroupConfig> list = new ArrayList<GroupConfig>();

                    while (rs.next()) {
                        GroupConfig groupConfig = new GroupConfig();

                        groupConfig.setGroupConfigId(rs.getLong(1));
                        groupConfig.setGroupCode(rs.getString(2));
                        groupConfig.setConfigCode(rs.getString(3));
                        groupConfig.setConfigValue(rs.getString(4));
                        groupConfig.setIsActive(rs.getBoolean(5));
                        list.add(groupConfig);
                    }

                    return list;
                }

            });

    int propCount = 1;
    Map<String, Map<String, String>> groups = new HashMap<String, Map<String, String>>();
    Map<String, String> temp = new HashMap<String, String>();

    for (GroupConfig config : groupConfigs) {
        temp.put(config.getConfigCode(), config.getConfigValue());
        if (propCount % 4 == 0) {
            groups.put(config.getGroupCode(), temp);
            temp = new HashMap<String, String>();
        }

        propCount++;
    }

    // Create TENANT dataSource
    Map<Object, Object> resolvedDataSources = new HashMap<Object, Object>();

    for (String tenantId : groups.keySet()) {

        Map<String, String> groupKV = groups.get(tenantId);
        DataSourceBuilder dataSourceBuilder = new DataSourceBuilder(this.getClass().getClassLoader());

        dataSourceBuilder.driverClassName(groupKV.get("DATASOURCE_CLASS")).url(groupKV.get("DB_URL"))
                .username(groupKV.get("USER_NAME")).password(groupKV.get("DB_PASSWORD"));

        //System.out.println(dataSourceBuilder.findType()); //class org.apache.tomcat.jdbc.pool.DataSource

        if (properties.getType() != null) {
            dataSourceBuilder.type(properties.getType());
        }

        if(LOGGER.isInfoEnabled())
            LOGGER.info("Building datasource : "+tenantId);
        resolvedDataSources.put(tenantId, dataSourceBuilder.build());

    }


    resolvedDataSources.put("MASTER", ds);


    MultitenantDataSource dataSource = new MultitenantDataSource();
    dataSource.setTargetDataSources(resolvedDataSources);
    dataSource.setDataSourceLookup(dataSourceLookup);       
    dataSource.afterPropertiesSet();

    if(LOGGER.isInfoEnabled())
        LOGGER.info("Datasources initialization finished !");

    return dataSource;
}

在控制器中,我将各自的数据源设置为(类似于 TENANT 数据源):

TenantContext.setCurrentTenant("MASTER");

问题: 在服务器启动时,一切正常(MASTER DB 和 TENANT 特定查询),但是一旦服务器空闲一段时间(几个小时),租户特定调用开始失败(而 MASTER数据库连接仍然可以正常工作)但有错误:

无法打开 JPA EntityManager 进行事务处理;嵌套异常为 javax.persistence.PersistenceException: com.microsoft.sqlserver.jdbc.SQLServerException: 连接已关闭。

请帮助我摆脱这个异常。提前致谢。

【问题讨论】:

标签: sql-server azure spring-boot sql-server-2012 multi-tenant


【解决方案1】:

我也得到了问题和解决方案:

为什么租户连接被关闭?因为 spring-boot 的自动配置(@ConfigurationProperties(prefix = "spring.datasource"))没有被应用到我创建的租户数据源上代码。

解决方法-我添加了设置tomcat连接池属性的新方法:

private DataSource buildDataSource(String driverClass, String url, String user, String pass){

    PoolProperties p = new PoolProperties();
    p.setUrl(url);
    p.setDriverClassName(driverClass);
    p.setUsername(user);
    p.setPassword(pass);
    p.setJmxEnabled(true);
    p.setTestWhileIdle(false);
    p.setTestOnBorrow(true); 
    p.setValidationQuery("SELECT 1");
    p.setTestOnReturn(false);
    p.setValidationInterval(30000);
    p.setTimeBetweenEvictionRunsMillis(30000);
    p.setMaxActive(100);
    p.setInitialSize(10);
    p.setMaxWait(10000);
    p.setRemoveAbandonedTimeout(60);
    p.setMinEvictableIdleTimeMillis(30000);
    p.setMinIdle(10);
    p.setLogAbandoned(true);
    p.setRemoveAbandoned(true);
    DataSource datasource = new DataSource();
    datasource.setPoolProperties(p);

    return  datasource;
}

这解决了我的问题。但我很想知道是否有办法在 spring-boot 中创建对象时应用 AutoConfigurations。

【讨论】:

    猜你喜欢
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多