持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情。
SpringBatch从入门到精通-2-StepScope作用域和用法【掘金日新计划】
SpringBatch从入门到精通-3-并行处理【掘金日新计划】
SpringBatch从入门到精通-3.2-并行处理-远程分区【掘金日新计划】
SpringBatch从入门到精通-3.3-并行处理-远程分区(消息聚合)【掘金日新计划】
SpringBatch从入门到精通-4 监控和指标【掘金日新计划】
SpringBatch从入门到精通-4.2 监控和指标-原理【掘金日新计划】
SpringBatch从入门到精通-5 数据源配置相关【掘金日新计划】
SpringBatch从入门到精通-5.2 数据源配置相关-原理【掘金日新计划】
SpringBatch从入门到精通-6 读和写处理【掘金日新计划】
SpringBatch从入门到精通-6.1 读和写处理-实战【掘金日新计划】
SpringBatch从入门到精通-6.2 读和写处理-实战2【掘金日新计划】
SpringBatch从入门到精通-6.3 读和写处理-实战3【掘金日新计划】
SpringBatch从入门到精通-6.4 读和写处理-实战4【掘金日新计划】
SpringBatch从入门到精通-7 常见的批处理的一些模式【掘金日新计划】
SpringBatch从入门到精通-8 数据处理【掘金日新计划】
SpringBatch从入门到精通-9 重复处理【掘金日新计划】
SpringBatch从入门到精通-10 重试处理【掘金日新计划】
SpringBatch从入门到精通-11 同步执行但提前返回结果【掘金日新计划】
springbatch整体的领域模型如下:
虽然该Job对象可能看起来像一个简单的步骤容器,但开发人员必须了解许多配置选项。Job此外,对于 a将如何运行以及在该运行期间如何存储其元数据,有许多考虑因素
配置作业
接口有多种实现Job。但是,构建器抽象出配置上的差异。
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.start(playerLoad())
.next(gameLoad())
.next(playerSummarization())
.build();
}
Job可重启性
执行批处理作业时的一个关键问题涉及Job重新启动时的行为。 如果 已经存在于特定的 ,则启动 Job被认为是“重新启动” 。理想情况下,所有作业都应该能够从中断的地方开始,但在某些情况下这是不可能的。在这种情况下创建新的完全取决于开发人员。*但是,Spring Batch 确实提供了一些帮助。如果 a jobInstance永远不应该重新启动,但应该始终作为 new 的一部分运行,那么 restartable 属性可以设置为“false”。
以下示例显示如何在 Java中将restartable字段设置为:false
Java 配置
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.preventRestart()
...
.build();
}
换句话说,将 restartable 设置为 false 意味着“这 Job不支持重新启动”。重新启动不可重新启动的Job会导致抛出JobRestartException。
Job job = new SimpleJob();
job.setRestartable(false);
JobParameters jobParameters = new JobParameters();
JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
jobRepository.saveOrUpdate(firstExecution);
try {
jobRepository.createJobExecution(job, jobParameters);
fail();
}
catch (JobRestartException e) {
// expected
}
这段 JUnit 代码展示了如何尝试 JobExecution为不可重新启动的作业创建第一次不会导致任何问题。但是,第二次尝试会抛出一个JobRestartException.
拦截作业执行
在 Job 的执行过程中,通知其生命周期中的各种事件可能很有用,以便可以执行自定义代码。SimpleJob允许通过在适当的时间调用 JobListener 来实现这 一点 :
public interface JobExecutionListener {
void beforeJob(JobExecution jobExecution);
void afterJob(JobExecution jobExecution);
}
JobListeners可以SimpleJob通过在作业上设置监听器来添加。
以下示例显示如何将侦听器方法添加到 Java 作业定义:
Java 配置
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.listener(sampleListener())
...
.build();
}
需要注意的是,afterJob无论成功与否,都会调用该方法Job。如果需要判断成败,可以从 中获取JobExecution,如下:
public void afterJob(JobExecution jobExecution){
if (jobExecution.getStatus() == BatchStatus.COMPLETED ) {
//job success
}
else if (jobExecution.getStatus() == BatchStatus.FAILED) {
//job failure
}
}
该接口对应的注解为:
- @BeforeJob
- @AfterJob
JobParametersValidator
在 XML 命名空间中声明或使用任何子类的 AbstractJob作业可以选择在运行时为作业参数声明验证器。例如,当您需要断言作业以其所有必需参数启动时,这很有用。有一个 DefaultJobParametersValidator可以用来约束简单的强制和可选参数的组合,对于更复杂的约束你可以自己实现接口。
通过 java builder 支持验证器的配置,如以下示例所示:
@Bean
public Job job1() {
return this.jobBuilderFactory.get("job1")
.validator(parametersValidator())
...
.build();
}
Java 配置
Spring 3 带来了通过 java 而不是 XML 配置应用程序的能力。从 Spring Batch 2.2.0 开始,可以使用相同的 java 配置来配置批处理作业。基于 java 的配置有两个组件:@EnableBatchProcessing 注解和两个构建器。
其@EnableBatchProcessing工作方式类似于 Spring 家族中的其他 @Enable* 注释。在这种情况下,@EnableBatchProcessing为构建批处理作业提供基本配置。StepScope在这个基本配置中,除了一些可用于自动装配的 bean 之外,还会创建一个实例:
- JobRepository:bean名称“jobRepository”
- JobLauncher:bean名称“jobLauncher”
- JobRegistry:bean名称“jobRegistry”
- PlatformTransactionManager: bean 名称 "transactionManager"
- JobBuilderFactory:bean名称“jobBuilders”
- StepBuilderFactory:bean名称“stepBuilders”
此配置的核心接口是BatchConfigurer. 默认实现提供了上面提到的 bean,并且需要 DataSource作为上下文中的 bean 来提供。JobRepository 使用此数据源。您可以通过创建接口的自定义实现来自定义这些 bean BatchConfigurer。通常,扩展DefaultBatchConfigurer(如果 未找到则提供BatchConfigurer)并覆盖所需的 getter 就足够了。但是,可能需要从头开始实施您自己的。以下示例显示了如何提供自定义事务管理器:
@Bean
public BatchConfigurer batchConfigurer(DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource) {
@Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
有了基本配置,用户就可以使用提供的构建器工厂来配置作业。JobBuilderFactory以下示例显示了使用和配置的两步作业 StepBuilderFactory:
@Configuration
@EnableBatchProcessing
@Import(DataSourceConfiguration.class)
public class AppConfig {
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Bean
public Job job(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2) {
return jobs.get("myJob").start(step1).next(step2).build();
}
@Bean
protected Step step1(ItemReader<Person> reader,
ItemProcessor<Person, Person> processor,
ItemWriter<Person> writer) {
return steps.get("step1")
.<Person, Person> chunk(10)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
protected Step step2(Tasklet tasklet) {
return steps.get("step2")
.tasklet(tasklet)
.build();
}
}
配置 JobRepository
使用时@EnableBatchProcessing,JobRepository为您提供开箱即用的一个。
如前所述,JobRepository用于 Spring Batch 中各种持久域对象的基本 CRUD 操作,例如 JobExecution和 StepExecution。许多主要框架功能都需要它,例如JobLauncher、 Job和Step.
使用 java 配置时,JobRepository为您提供了一个。如果提供了 datasource,则提供了一个基于 JDBC 的开箱即用,如果没有DataSource提供,则提供基于 JDBC 的Map。但是,您可以通过接口JobRepository的实现来自 定义配置BatchConfigurer。
Java 配置
...
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setIsolationLevelForCreate("ISOLATION_SERIALIZABLE");
factory.setTablePrefix("BATCH_");
factory.setMaxVarCharLength(1000);
return factory.getObject();
}
...
除了 dataSource 和 transactionManager 之外,上面列出的配置选项都不是必需的。如果未设置,将使用上面显示的默认值。出于提高认识的目的,它们在上面显示。[最大 varchar 长度默认为 2500,这是示例架构脚本中长VARCHAR列 的最大长度
JobRepository 的事务配置
如果使用命名空间或提供的命名空间FactoryBean,则会自动围绕存储库创建事务建议。这是为了确保批处理元数据(包括故障后重新启动所需的状态)正确持久化。如果存储库方法不是事务性的,那么框架的行为就没有很好的定义。方法属性中的隔离级别create是单独指定的,以确保在启动作业时,如果两个进程尝试同时启动同一个作业,则只有一个成功。该方法的默认隔离级别是SERIALIZABLE,这是非常激进的。READ_COMMITTED也可以。READ_UNCOMMITTED如果两个进程不太可能以这种方式发生冲突,那就没问题了。然而,由于调用create方法很短,不太可能 SERIALIZED出问题,只要数据库平台支持。但是,这可以被覆盖。
以下示例显示了如何覆盖 Java 中的隔离级别:
Java 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ");
return factory.getObject();
}
如果不使用名称空间或工厂 bean,那么使用 AOP 配置存储库的事务行为也是必不可少的。
以下示例显示了如何在 Java 中配置存储库的事务行为:
Java 配置
@Bean
public TransactionProxyFactoryBean baseProxy() {
TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
Properties transactionAttributes = new Properties();
transactionAttributes.setProperty("*", "PROPAGATION_REQUIRED");
transactionProxyFactoryBean.setTransactionAttributes(transactionAttributes);
transactionProxyFactoryBean.setTarget(jobRepository());
transactionProxyFactoryBean.setTransactionManager(transactionManager());
return transactionProxyFactoryBean;
}
更改表前缀
另一个可修改的属性JobRepository是元数据表的表前缀。默认情况下,它们都以BATCH_. BATCH_JOB_EXECUTION并且 BATCH_STEP_EXECUTION是两个例子。但是,有可能修改此前缀的原因。如果模式名称需要添加到表名称之前,或者如果同一模式中需要多组元数据表,则需要更改表前缀:
Java 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setTablePrefix("SYSTEM.TEST_");
return factory.getObject();
}
鉴于前面的更改,对元数据表的每个查询都带有前缀 SYSTEM.TEST_。BATCH_JOB_EXECUTION被称为系统。TEST_JOB_EXECUTION.
只有表前缀是可配置的。表名和列名不是。
内存存储库
在某些情况下,您可能不想将域对象持久化到数据库中。一个原因可能是速度。在每个提交点存储域对象需要额外的时间。另一个原因可能是您不需要为特定工作保留状态。出于这个原因,Spring 批处理提供了Map作业存储库的内存版本。
以下示例显示了MapJobRepositoryFactoryBeanJava 中的包含:
Java 配置
// This would reside in your BatchConfigurer implementation
@Override
protected JobRepository createJobRepository() throws Exception {
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean();
factory.setTransactionManager(transactionManager);
return factory.getObject();
}
请注意,内存存储库是易失性的,因此不允许在 JVM 实例之间重新启动。它也不能保证两个具有相同参数的作业实例同时启动,并且不适合在多线程作业或本地分区的作业中使用Step。因此,只要您需要这些功能,就可以使用存储库的数据库版本。
然而,它确实需要定义事务管理器,因为存储库中有回滚语义,并且因为业务逻辑可能仍然是事务性的(例如 RDBMS 访问)。出于测试目的,许多人发现它 ResourcelessTransactionManager很有用。
和相关的MapJobRepositoryFactoryBean类在 v4 中已被弃用,并计划在 v5 中删除。如果要使用内存中的作业存储库,可以使用嵌入式数据库,如 H2、Apache Derby 或 HSQLDB。有几种方法可以创建嵌入式数据库并在 Spring Batch 应用程序中使用它。一种方法是使用Spring JDBC中的 API :
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("/org/springframework/batch/core/schema-drop-h2.sql") .addScript("/org/springframework/batch/core/schema-h2.sql")
.build();
}
在应用程序上下文中将嵌入式数据源定义为 bean 后,如果使用@EnableBatchProcessing. 否则,您可以使用基于 JDBC 的手动配置它JobRepositoryFactoryBean,如配置 JobRepository 部分所示。
存储库中的非标准数据库类型
如果您使用的数据库平台不在受支持的平台列表中,则您可以使用其中一种受支持的类型,前提是 SQL 变体足够接近。为此,您可以使用 rawJobRepositoryFactoryBean而不是命名空间快捷方式,并使用它将数据库类型设置为最接近的匹配。
以下示例显示了如何使用JobRepositoryFactoryBean 将数据库类型设置为最接近的匹配:
Java 配置
配置 JobLauncher
使用时@EnableBatchProcessing,JobRegistry为您提供开箱即用的一个。本节介绍如何配置您自己的。
JobLauncher接口的最基本实现是SimpleJobLauncher. 它唯一需要的依赖项是 a JobRepository,以便获得执行。
以下示例显示了SimpleJobLauncherJava 中的 a:
Java 配置
...
// This would reside in your BatchConfigurer implementation
@Override
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
...
获取到JobExecution后,将其传递给Job的 execute 方法,最终返回JobExecution给调用者,如下图所示:
图 2. Job Launcher 序列
该序列很简单,并且在从调度程序启动时运行良好。但是,尝试从 HTTP 请求启动时会出现问题。在这种情况下,启动需要异步完成,以便SimpleJobLauncher立即返回给它的调用者。这是因为在长时间运行的进程(例如批处理)所需的时间内保持 HTTP 请求打开并不是一个好习惯。下图显示了一个示例序列:
图 3. 异步作业启动器序列
SimpleJobLauncher可以通过 配置TaskExecutor.
以下 Java 示例显示了一个SimpleJobLauncher配置为立即返回:
Java 配置
@Bean
public JobLauncher jobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository());
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
springTaskExecutor 接口的任何实现都可以用来控制作业的异步执行方式。
运行作业
启动批处理作业至少需要两件事: Job要启动的和 JobLauncher. 两者都可以包含在相同的上下文或不同的上下文中。例如,如果从命令行启动作业,将为每个作业实例化一个新的 JVM,因此每个作业都有自己的JobLauncher. 但是,如果在 范围内的 Web 容器中运行, HttpRequest通常会有一个 JobLauncher配置为异步作业启动,多个请求将调用该容器来启动它们的作业。
从命令行运行作业
对于想要从企业调度程序运行其作业的用户,命令行是主要界面。这是因为大多数调度程序(Quartz 除外,除非使用 NativeJob)直接与操作系统进程一起工作,主要由 shell 脚本启动。除了 shell 脚本之外,还有很多方法可以启动 Java 进程,例如 Perl、Ruby,甚至是 ant 或 maven 等“构建工具”。但是,由于大多数人都熟悉 shell 脚本,因此本示例将重点介绍它们。
CommandLineJobRunner
因为启动作业的脚本必须启动 Java 虚拟机,所以需要有一个带有 main 方法的类作为主要入口点。Spring Batch 提供了一个实现这个目的的实现: CommandLineJobRunner. 需要注意的是,这只是引导应用程序的一种方式,但启动 Java 进程的方式有很多种,而且绝不应将此类视为确定性的。执行CommandLineJobRunner 四项任务:
- 加载适当的 ApplicationContext
- 将命令行参数解析为 JobParameters
- 根据参数找到合适的工作
- 使用JobLauncher应用程序上下文中提供的启动作业。
所有这些任务都只使用传入的参数来完成。以下是必需的参数:
| jobpath | 将用于创建ApplicationContext. 该文件应包含运行完整作业所需的所有内容 |
|---|---|
| jobname | 要运行的作业的名称。 |
这些参数必须首先传入路径,然后传入名称。这些之后的所有参数都被认为是作业参数,转换为 JobParameters 对象,并且必须采用“name=value”的格式。
以下示例显示了作为作业参数传递给 Java 中定义的作业的日期:
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05
在大多数情况下,您会希望使用清单在 jar 中声明您的主类,但为简单起见,直接使用该类。第一个参数是“io.spring.EndOfDayJobConfiguration”,它是包含作业的配置类的完全限定类名。第二个参数“endOfDay”表示作业名称。最后一个参数 'schedule.date(date)=2007/05/05' 被转换为一个 JobParameters对象。java配置的一个例子如下:
以下示例显示了endOfDayJava 中的示例配置:
@Configuration
@EnableBatchProcessing
public class EndOfDayJobConfiguration {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job endOfDay() {
return this.jobBuilderFactory.get("endOfDay")
.start(step1())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.tasklet((contribution, chunkContext) -> null)
.build();
}
}
代码位置: github.com/jackssybin/…