【问题标题】:Spring Batch Multiple ThreadsSpring Batch 多线程
【发布时间】:2015-02-01 09:26:04
【问题描述】:

我正在编写一个 Spring Batch,并在需要时对其进行缩放。 我的 ApplicationContext 看起来像这样

@Configuration
@EnableBatchProcessing
@EnableTransactionManagement
@ComponentScan(basePackages = "in.springbatch")
@PropertySource(value = {"classpath:springbatch.properties"})

public class ApplicationConfig {

@Autowired
Environment environment;

@Autowired
private JobBuilderFactory jobs;

@Autowired
private StepBuilderFactory stepBuilderFactory;

@Bean
public Job job() throws Exception {

    return jobs.get("spring_batch")
            .flow(step()).end()
            .build();
}

@Bean(name = "dataSource", destroyMethod = "close")
public DataSource dataSource() {

    BasicDataSource basicDataSource = new BasicDataSource();



    return basicDataSource;
}

@Bean
public JobRepository jobRepository() throws Exception {
    JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
    jobRepositoryFactoryBean.setTransactionManager(transactionManager());
    jobRepositoryFactoryBean.setDataSource(dataSource());
    return jobRepositoryFactoryBean.getObject();
}

 @Bean(name = "batchstep")
 public Step step() throws Exception {

    return    stepBuilderFactory.get("batchstep").allowStartIfComplete(true).
    transactionManager(transactionManager()).
          chunk(2).reader(batchReader()).processor(processor()).writer(writer()).build();

  }



@Bean
ItemReader batchReader() throws Exception {
    System.out.println(Thread.currentThread().getName()+"reader");
    HibernateCursorItemReader<Source> hibernateCursorItemReader = new HibernateCursorItemReader<>();
    hibernateCursorItemReader.setQueryString("from Source");
    hibernateCursorItemReader.setFetchSize(2);
    hibernateCursorItemReader.setSessionFactory(sessionFactory().getObject());


    hibernateCursorItemReader.close();
    return hibernateCursorItemReader;
}

@Bean
 public ItemProcessor processor() {
     return new BatchProcessor();
 }

@Bean
public ItemWriter writer() {
    return new BatchWriter();
}

public TaskExecutor taskExecutor(){

    SimpleAsyncTaskExecutor asyncTaskExecutor=new SimpleAsyncTaskExecutor("spring_batch");
    asyncTaskExecutor.setConcurrencyLimit(5);
    return asyncTaskExecutor;


}
@Bean
public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setPackagesToScan(new String[]{"in.springbatch.entity"});
    sessionFactory.setHibernateProperties(hibernateProperties());

    return sessionFactory;
}

@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
    return new PersistenceExceptionTranslationPostProcessor();
}

@Bean
@Autowired
public HibernateTransactionManager transactionManager() {
    HibernateTransactionManager txManager = new HibernateTransactionManager();
    txManager.setSessionFactory(sessionFactory().getObject());

    return txManager;
}

Properties hibernateProperties() {
    return new Properties() {
        {
            setProperty("hibernate.hbm2ddl.auto",       environment.getProperty("hibernate.hbm2ddl.auto"));
            setProperty("hibernate.dialect", environment.getProperty("hibernate.dialect"));
            setProperty("hibernate.globally_quoted_identifiers", "false");

        }
    };
}

}

  1. 通过上述配置,我可以从 DB 读取、处理数据并写入 DB。
  2. 我使用块大小为 2 并使用从游标读取 2 条记录 HibernateCursorItem 阅读器和我从数据库中读取的查询是基于 date 选择当前日期记录。
  3. 到目前为止,我能够实现所需的行为以及重新启动 仅处理未处理的作业拣货记录的能力 由于之前的运行失败。

现在我的要求是批量使用多个线程来处理数据并写入数据库。

我的处理器和写入器看起来像这样

@Component
public class BatchProcessor implements ItemProcessor<Source,DestinationDto>{

@Override
public DestinationDto process(Source source) throws Exception {

        System.out.println(Thread.currentThread().getName()+":"+source);
        DestinationDto destination=new DestinationDto();
        destination.setName(source.getName());
        destination.setValue(source.getValue());
        destination.setSourceId(source.getSourceId().toString());

    return destination;
}
@Component
public class BatchWriter implements ItemWriter<DestinationDto>{

@Autowired
IBatchDao batchDao;

@Override
public void write(List<? extends DestinationDto> list) throws Exception {
   System.out.println(Thread.currentThread().getName()+":"+list);
    batchDao.saveToDestination((List<DestinationDto>)list);
}

我更新了我的步骤并添加了一个 ThreadPoolTask​​Executor 如下

@Bean(name = "batchstep")
public Step step() throws Exception {

    return  stepBuilderFactory.get("batchstep").allowStartIfComplete(true).
     transactionManager(transactionManager()).chunk(1).reader(batchReader()).
     processor(processor()).writer(writer()).taskExecutor(taskExecutor()).build();

  }

在此之后,我的处理器被多个线程调用但具有相同的源数据。 还有什么需要我做的吗?

【问题讨论】:

    标签: java multithreading spring spring-batch


    【解决方案1】:

    这是个大问题

    1. 最好的办法是查看 Spring Batch 文档 (Here) 中的 Scaling and Parallel Processing 一章

    2. spring 批处理示例中可能存在一些多线程示例 (Here)

    3. 线程化 Spring 批处理作业的一种简单方法是创建一个未来处理器 - 您将所有处理逻辑放在一个未来对象中,而您的 spring-processor 类只将对象添加到未来。您的作家类然后等待未来完成,然后再执行写入过程。抱歉,我也没有样本可以为您指出这一点 - 但如果您有具体问题,我可以尝试回答!

    【讨论】:

    • 感谢您抽出时间阅读一个冗长的问题。我认为首先我应该花一些时间尝试 Spring Batch 开箱即用的功能来实现多线程
    • 对您的用例来说实际上是 - 选项 3 可能更容易。当您可以对整个步骤进行多线程处理时,即您的阅读器可以分块读取信息,Spring Batch 开箱即用的功能可以很好地实现多线程工作
    • 好的,我可以尝试选项 3。但是正如您所说的“Spring Batch 开箱即用的功能可以很好地实现多线程,当您可以对整个步骤进行多线程时,即您的读者可以阅读块中的信息“。我正在从 HibernateCursorReader 读取数据,它为我提供了分块读取数据的选项。这是正确的,那么你能指出我,我怎样才能多线程我的步骤?
    • 如果您可以在阅读器级别进行分块(我不确定 HibernateCursorReader 是否提供开箱即用的功能),但您通常可以编写查询来分块。在这种情况下最简单的方法 - “启动并行处理的最简单方法是将 TaskExecutor 添加到您的 Step 配置中,例如作为 tasklet 的属性:” - 7.1 Multi-threaded Step 例如工作 - here 特别是在这个例子中,“加载”步骤是多线程的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 2018-08-17
    • 2021-02-20
    • 2016-04-15
    相关资源
    最近更新 更多