为什么Spring Batch为每个线程使用1个数据库连接?

时间:2018-03-05 21:28:28

标签: java multithreading spring-boot spring-batch hikaricp

为什么Spring Batch为每个线程使用1个数据库连接?

堆栈:

  • Java 8
  • Spring Boot 1.5
  • Spring Batch 3.0.7
  • HikariCP 2.7.6

DataSource config:

  • batcdb(postgres)
  • readdb(oracle)
  • writedb(postgres)

每个数据源都使用HikariCP,每个数据源默认有10个连接。

Spring Batch配置: ThreadExecutor-1:

core-pool-size: 10
max-pool-size: 10
throttle-limit: 10

Job-1 Config / ThreadPoolTask​​Executor: (通过application.yml设置的池大小和限制)

@Bean
public Step job1Step() {
    return stepBuilderFactory.get("job1Step")
            .<ReadModel, WriteModel>chunk(chunkSize)
            .reader(itemReader())
            .processor(compositeProcessor())
            .writer(itemWriter())
            .faultTolerant()
            .taskExecutor(job1TaskExecutor())
            .throttleLimit(throttleLimit)
            .build();
}

@Bean
public ThreadPoolTaskExecutor job1TaskExecutor() {
     ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
     pool.setCorePoolSize(poolSize);
     pool.setMaxPoolSize(maxPoolSize);
     pool.setWaitForTasksToCompleteOnShutdown(false);
     return pool;
 }

@Bean
@StepScope
public Job1ItemReader job1ItemReader() {
    return new Job1ItemReader(readdb, pageSize);
}

Job1-ItemReader的缩写代码

public class Job1ItemReader extends JdbcPagingItemReader<ReadModel> {
...
}

ThreadExecutor-2:

core-pool-size: 5
max-pool-size: 5
throttle-limit: 5

Job-2 Config / ThreadPoolTask​​Executor:

@Bean
public Step job2Step() throws Exception {
    return stepBuilderFactory.get("job2Step")
            .<ReadModel2, WriteModel2>chunk(chunkSize)
            .reader(job2ItemReader())
            .processor(job2CompositeProcessor())
            .writer(job2ItemWriter())
            .faultTolerant()
            .taskExecutor(job2TaskExecutor())
            .throttleLimit(throttleLimit)
            .build();
}

@Bean
public ThreadPoolTaskExecutor job2TaskExecutor() {
    ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
    pool.setCorePoolSize(corePoolSize);
    pool.setMaxPoolSize(maxPoolSize);
    pool.setQueueCapacity(queueCapacity);
    pool.setWaitForTasksToCompleteOnShutdown(false);
    return pool;
}

@Bean
@StepScope
public Job2ItemReader job2ItemReader() {
    return new Job2ItemReader(readdb, pageSize);    
}

Job2-ItemReader的缩写代码

public class Job2ItemReader extends JdbcPagingItemReader<ReadModel2> {
...
}
  • 有2个职位
  • Job-1长期运行(多天)
  • Job-2通常在一小时或两小时内完成,并且每天按计划运行
  • 作业位于同一个“应用程序”中,在同一个JVM上运行
  • 每个Job都有自己定义的ThreadPoolTask​​Executor

当Job-1正在运行且Job-2启动时,Job-2无法与readdb建立连接。 Job-2的批处理读取器抛出以下错误。

Caused by: org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-3 - Connection is not available, request timed out after 30000ms.
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:339)
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:366)
at org.springframework.batch.support.DatabaseType.fromMetaData(DatabaseType.java:97)
at org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean.getObject(SqlPagingQueryProviderFactoryBean.java:158)
... 30 common frames omitted
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLTransientConnectionException: HikariPool-3 - Connection is not available, request timed out after 30000ms.
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:326)
... 33 common frames omitted
Caused by: java.sql.SQLTransientConnectionException: HikariPool-3 - Connection is not available, request timed out after 30000ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:666)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:182)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)

(为保护无辜而编辑)

价:

1 个答案:

答案 0 :(得分:4)

Spring Batch每个线程使用一个数据库连接(在某些情况下实际上可以使用更多)的原因是由于事务。 Spring事务与线程相关联。 Spring Batch中的所有内容都发生在事务中。因此,当您使用单个线程进行单个作业时,您最多只能使用几个连接。但是,如果您有多线程步骤,则每个线程至少需要一个连接用于事务处理。