Spring Batch 2.1.8 中文文档(五)

3.2 Step

        Step是一个领域对象,它封装了批处理任务中的一个独立的连续阶段。因此每个job是由一个或是多个step组成的,step包含了定义以及控制一个实际运行中批处理任务所必须的所有信息。这个描述很含糊,是因为step中的内容完全是编写job的开发者所赋予的,step的复杂度完全依赖于开发者。一个简单的step可能只是从文件中加载数据到数据库,只需要几行代码(依赖于实现方式)。一个复杂的step可能作为整个业务处理的一部分而有着复杂的业务流程。像job那样,step有着自己的StepExecution并且对应于唯一一个JobExecution:

3.2.1 StepExecution

StepExecution表示需要执行一个step,和JobExecution类似,在每次运行step时会创建一个新的StepExecution。但是,如果一个Step之前的那个step执行失败导致这个step无法执行,则不会为这个step创建对应的StepExecution,因为StepExecution只会在step实际启动时创建。

Step的执行过程是由StepExecution类的对象所表示的,包括了每次执行所对应的step、JobExecution、相关的事务操作(例如提交与回滚)、开始时间结束时间等。此外每次执行step时还包含了一个ExecutionContext,用来存放开发者在批处理运行过程中所需要的任何信息,例如用来重启的静态数据与状态数据。下表列出了StepExecution的属性:

status 使用BatchStatus对象来表示执行状态。运行时,状态为BatchStatus.STARTED;运行失败状态为BatchStatus.FAILED;成功结束时状态为BatchStatus.COMPLETED
startTime 执行开始时间,使用java.util.Date表示
endTime 执行结束时间(成功或失败),使用java.util.Date表示
exitStatus 执行结果,使用ExitStatus表示。最重要的是它包含了返回给调用者的退出代码。查看第五章有更多细节。
executionContext 包含了在执行过程中任何需要进行持久化的用户数据
readCount 成功读取的记录数
writeCount 成功写入的记录数
commitCount 执行过程的事务中成功提交次数
rollbackCount 执行过程的事务中回滚次数
readSkipCount 因为读取失败而略过的记录数
processSkipCount 因为处理失败而略过的记录数
filterCount 被ItemProcessor过滤的记录数
writerSkipCount 因为写入失败而略过的记录数

3.3 ExecutionContext

ExecutionContext是一组框架持久化与控制的key/value对,能够让开发者在StepExecution或JobExecution范畴保存需要进行持久化的状态,它同QuartZ的JobDataMap是相似的。任务重启就是最好的例子。以一个平面文件输入为例,在处理单行时,框架会在每次commit之后持久化ExecutionContext,这样ItemReader万一在运行过程中遇到问题,甚至是掉电,也能够存储之前的状态。而完成这些只需要把当前读取的行数放入context中,框架就会完成剩下的:

executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());
使用之前的EndOfDay例子,假设有这么一部:'loadData',从数据库中加载文件。在第一次失败后,元数据表应该是这样:

Table 3.9  BATCH_JOB_INSTANCE

JOB_INST_ID JOB_NAME
1 EndOfDayJob

Table 3.10 BATCH_JOB_PARAMS

JOB_INST_ID TYPE_CD KEY_NAME DATE_VAL
1 DATE scheduler.Date 2008-01-01

Table 3.11 BATCH_JOB_EXECUTION

JOB_EXEC_ID JOB_INST_ID START_TIME END_TIME STATUS
1 1 2008-01-01 21:00 2008-01-01 21:30 FAILED

Table 3.12 BATCH_STEP_EXECUTION

STEP_EXEC_ID JOB_EXEC_ID STEP_NAME START_TIME END_TIME STATUS
1 1 loadDate 2008-01-01 21:00 2008-01-01 21:30 FAILED

Table 3.13 BATCH_STEP_EXECUTION_CONTEXT

STEP_EXEC_ID SHORT_CONTEXT
1 {piece.count=40321}

在这个例子中,运行30分钟处理了40321条记录,框架在每次提交前会更新这个数字,并且ExecutionContext可以包含多个相关条目。如果要在提交前进行通知,还需要一个StepListener变量或是一个ItemStream变量,这个在随后的章节中会有更详细的讨论。就之前的这个例子,假定job在第二天重启,在重启时会从数据库中读取前一次运行保存下来的数据重组成ExecutionContext,打开ItemReader的时候,ItemReader会检查context中保存的状态并使用这些状态进行初始化:

if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
    log.debug("Initializing for restart. Restart data is: " + executionContext);

    long lineCount = executionContext.getLong(getKey(LINES_READ_COUNT));

    LineReader reader = getReader();

    Object record = "";
    while (reader.getPosition() < lineCount && record != null) {
        record = readLine();
    }
}

在上面的代码执行完成后,记录下当前行是40322,这样下次启动时就能从这一行开始继续执行。ExecutionContext也能用来统计需要保存的运行内容,例如,一个文件需要处理订单,而一条订单可能保存为多行记录,此时需要记录处理了多少条订单(订单数与文件行数不同)并在任务处理完成后把处理的订单总数用邮件发送出去,框架会为开发者把这些数据记录到当前运行的这个JobInstance的scope中。另外,判断ExecutionContext的使用时机是比较困难的。例如使用上面的'EndOfDay'例子,当01-01任务在01-02再次运行时,框架会认识到这是一个相同的JobInstance中一个不同的Step,于是从数据库中取出ExecutionContext作为Step的一部分StepExecution内容;相反对于01-02任务框架要认识到这是一个不同的instance,于是会给step一个空的context。框架需要为工程师做各种判断以保证在准确的时间有准确的状态。还需要重视的是在任何时间对于每个StepExecution只有一个ExecutionContext,因为创建的是一个共有的keyspace,ExecutionContext的客户端需要小心放置key-value数据以确保没有数据被覆盖。当然如果step没有在Context中保存数据,自然也不会被框架造成不利的影响。

一个JobExecution至少有一个ExecutionContext,所有StepExecution共用一个ExecutionContext。像代码所示:

ExecutionContext ecStep = stepExecution.getExecutionContext();
ExecutionContext ecJob = jobExecution.getExecutionContext();
//ecStep does not equal ecJob

ecStep不等于ecJob,他们是两个不同的ExecutionContext。step范围的ExecutionContext在Step被提交时保存,job范围的ExecutionContext在两个step执行之间保存。

3.4 JobRepository

JobRepository是上面所有概念的持久化机制,为JobLauncher,Job和Step提供了CRUD实现。当一个Job第一次启动时,从仓库中获取一个JobExecution,之后在整个执行过程中StepExecution和JobExecution都被这样持久化到仓库中:

<job-repository id="jobRepository"/>


3.5 JobLauncher

JobLauncher是使用取得的参数启动Job的简单接口:

public interface JobLauncher {

    public JobExecution run(Job job, JobParameters jobParameters) 
                throws JobExecutionAlreadyRunningException, JobRestartException;
}

此处希望从JobRepository获取一个合法的JobExecution并执行之。

3.6 Item Reader

ItemReader是一个抽象概念,用于表示step读取数据,一次读取一条。当ItemReader读完了所有数据,它会返回一个null值。更多的细节可以参考"Chapter 6 ItemReader and ItemWriter"。

3.7 Item Writer

ItemWriter是一个抽象概念,用于表示step输出数据,一次输出一批。通常情况下,ItemWriter只有在数据进入ItemWriter时,才知道输入的数据内容。更多细节可以参考"Chapter 6 ItemReader and ItemWriter"。

3.8 Item Processor

ItemProcessor是一个抽象概念,用于表示item的业务处理。ItemReader读数据,ItemWriter写数据,ItemProcessor能够转换数据或是处理业务逻辑。如果在处理过程中数据不合法,则会返回null值表示数据没有输出。更多细节可以参考"Chapter 6 ItemReader and ItemWriter"。

3.9 Batch Namespace

上面的许多领域概念都需要在spring的ApplicationContext中配置。但是如果这些接口的实现使用标准的bean定义,那么namespace提供了更容易的配置方式:

<beans:beans xmlns="http://www.springframework.org/schema/batch" 
     xmlns:beans="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="
           http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/batch 
           http://www.springframework.org/schema/batch/spring-batch-2.0.xsd">

    <job id="ioSampleJob">
        <step id="step1">
            <tasklet>
                <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
            </tasklet>
        </step>
    </job>

</beans:beans>

只要批处理的namespace申明过,那么这些元素就能够使用。更多配置job的信息参考"Chapter4 Configuring and Running a Job。更多配置step的信息参考"Chapter 5,Configuring a Step"。


猜你喜欢

转载自blog.csdn.net/shorn/article/details/7925935