在一些空闲时间后连接关闭错误

时间:2018-05-25 11:25:29

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

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

@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:连接已关闭。

请帮我摆脱这个例外。提前谢谢。

1 个答案:

答案 0 :(得分:0)

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

为什么租户连接会被关闭?因为spring-boot的自动配置(@ConfigurationProperties(prefix =“spring.datasource”))没有应用于我创建的租户DataSources代码。

解决方法 - 我添加了新方法来设置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中创建对象时是否有办法应用AutoConfiguration。