手动更改Spring Batch Meta-Data表是不是一个坏主意?

时间:2014-04-01 01:32:04

标签: java spring-batch

背景

我使用的是Spring Batch 2.1.8,并按CommandLineJobRunner运行作业。如:

java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:launchContext.xml theJobId

问题

在某些情况下,例如服务器崩溃,可能会中断正在运行的作业。 但中断的作业在Spring Batch Meta-Data表中保留了STARTED状态,并且无法再次运行。

org.springframework.batch.core.repository.JobExecutionAlreadyRunningException: A job execution for this job is already running

我可以想到两个解决方案:

解决方法1

添加一个新的作业参数并每次更改它以使其成为" new" Spring Batch的工作。如:

java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:launchContext.xml theJobId times=0

当需要重新运行时,请清除所有相应的输出数据,计算一次times,然后重新运行该作业。

溶液2

手动更改Spring Batch元数据表。

更新状态以使作业可重新启动。如:

UPDATE BATCH_JOB_EXECUTION SET END_TIME = SYSTIMESTAMP, STATUS = 'FAILED', EXIT_CODE = 'FAILOVER' WHERE JOB_EXECUTION_ID =
    (SELECT MAX(JOB_EXECUTION_ID) FROM BATCH_JOB_EXECUTION WHERE JOB_INSTANCE_ID =
        (SELECT MAX(JOB_INSTANCE_ID) FROM BATCH_JOB_INSTANCE WHERE JOB_NAME = 'XXX'));

我已经尝试过,看起来效果很好。

问题

Solution2是个坏主意吗?有没有陷阱?

提前致谢。并且赞赏任何其他解决方案。

2 个答案:

答案 0 :(得分:5)

解决方案2是目前公认的方法。 API不提供修复此方案的方法。过去曾有人要求框架自动清理,但99%的情况下,需要做出人为决定,以确定是否确实需要进行清理。

我对选项2的唯一注意事项是检查BATCH_STEP_EXECUTION表以及查看最后执行步骤的状态。

答案 1 :(得分:3)

我为此创建了一个特定的spring bean,它在容器刷新时触发(在app(re)启动时也会发生)。

它搜索“正在运行”的作业,将它们标记为“FAILED”并重新启动它们。

import java.util.Date;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class BatchJobRestarter implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOGGER  = LoggerFactory.getLogger(BatchJobRestarter.class);

    @Autowired
    private JobExplorer         jobExplorer;

    @Autowired
    JobRepository               jobRepository;

    @Autowired
    JobOperator                 jobOperator;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        LOGGER.info("Container restart: restarting 'running' batch jobs");
        List<String> jobs = jobExplorer.getJobNames();
        for (String job : jobs) {
            Set<JobExecution> runningJobs = jobExplorer.findRunningJobExecutions(job);

            for (JobExecution runningJob : runningJobs) {
                try {
                    LOGGER.info("Restarting job {} with parameters {}", runningJob.getJobInstance().getJobName(), runningJob.getJobParameters().toString());
                    runningJob.setStatus(BatchStatus.FAILED);
                    runningJob.setEndTime(new Date());
                    jobRepository.update(runningJob);
                    jobOperator.restart(runningJob.getId());
                } catch (Exception e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
    }
}

Steef