春季批处理调度期间出现的错误

Posted

技术标签:

【中文标题】春季批处理调度期间出现的错误【英文标题】:bugs accuring during spring batch scheduling 【发布时间】:2020-07-11 12:09:48 【问题描述】:

嘿,伙计们,我正在使用调度(使用 cron 触发器)进行春季批处理,它正在工作,但存在以下错误:

假设 cron 值每 10 秒启动一次批处理,当我启动第一个和之后,例如 3 秒,我启动另一个,spring 不会意识到 3 秒的间隙,它会启动它们就像我同时触发了它们一样

这是我的代码

这是我将要启动的工作的类别

@Component
public class JobThread implements Runnable 

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    @Lazy
    private Job job;

    public JobParameters jobParameters;

    private Logger log = Logger.getLogger(JobThread.class);

    public synchronized void runBatch() 

        jobParameters = new JobParametersBuilder().addLong("LaunchTime", System.currentTimeMillis())
                .addString("TenantID", BatchController.getCurrentTenant().get()).toJobParameters();

        try 
            JobExecution jobExecution = jobLauncher.run(job, jobParameters);
            log.info("Job's Status:::" + jobExecution.getStatus());
         catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
                | JobParametersInvalidException e) 
            e.printStackTrace();
        
    

    @Override
    public void run() 
        this.runBatch();

    



将调用作业的控制器

@RestController
@RequestMapping("tenant/batch")
public class BatchController 

    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;

    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private JobThread jobThread;

    private static ThreadLocal<String> currentTenant;

    @PostMapping("/schedule")
    public void setBatch(@RequestBody BatchBean cron) 

        currentTenant = new ThreadLocal<String>() 
            @Override
            protected String initialValue() 
                new TenantContext();
                return TenantContext.getCurrentTenant();
            
        ;

        //cron = "*/10 * * * * *";


        taskScheduler.schedule(taskExecutor.createThread(jobThread), new CronTrigger(cron.getCron()));

    

我希望我已经足够清楚了 提前致谢

【问题讨论】:

您的代码有缺陷且危险。您将状态保持在单例中并覆盖该状态永远不要这样做。不要重新创建您的ThreadLocal,也不要重新创建您的JobParamaters。只剩下最后一个。所以很危险。此外,您应该对接口而不是具体实现进行编程。不需要为可运行对象创建线程,因为您的 JobThread 已经是 Runnable 【参考方案1】:

问题在于您的代码不是线程安全的,因此存在潜在危险。此外,您的ThreadLocal 将无法工作,因为该作业将在不同的线程中执行,并且无法访问ThreadLocal

    不要在控制器中重新创建ThreadLocal。定义一次,然后保持原样。 您的JobThread 是一个保持状态(参数)的单例,因此只保留最后一个。 编程接口 TaskScheduler 而不是具体实现 不要创建线程,因为您的 JobThread 已经是 Runnable。 不要将JobThread 设为单例,而是根据需要构造一个新的并传入所需的参数。

您的JobThread 应该是这样的。

public class JobThread implements Runnable 

    private final Logger log = Logger.getLogger(JobThread.class);
    private final JobLauncher jobLauncher;
    private final Job job;
    private final String tenant;

    public JobThread(JobLauncher launcher, Job job, String tenant) 
      this.jobLauncher=launcher;
      this.job=job;
      this.tenant=tenant;
    

    @Override
    public void run() 

        JobParameters jobParameters = new JobParametersBuilder()
                          .addLong("LaunchTime", System.currentTimeMillis())
                          .addString("TenantID", tenant);

        try 
            JobExecution jobExecution = jobLauncher.run(job, jobParameters);
            log.info("Job's Status:::" + jobExecution.getStatus());
         catch (JobExecutionException e) 
            log.error(e.getMessage(), e);
        
    

然后在您的控制器中注入所需的JobLauncerJob。需要时构造一个新的JobThread 并传入所需的信息。

@RestController
@RequestMapping("tenant/batch")
public class BatchController 

    @Autowired
    private TaskScheduler taskScheduler;
    @Autowired
    private JobLauncher jobLauncher;
    @Autowired
    @Lazy
    private Job job;

    @PostMapping("/schedule")
    public void setBatch(@RequestBody BatchBean cron) 

        //cron = "*/10 * * * * *";
        String tenant = TenantContext.getCurrentTenant();
        JobThread task = new JobThread(this.jobLauncher, this.job, tenant);        
        taskScheduler.schedule(task, new CronTrigger(cron.getCron()));
    

最后一点,System.currentTimeMillis 的精度可能因您的操作系统/系统/架构而异。见上述方法的javadoc。

【讨论】:

谢谢您的回答和您的笔记

以上是关于春季批处理调度期间出现的错误的主要内容,如果未能解决你的问题,请参考以下文章

在我的春季批处理中添加 Maven 依赖项会出现特定错误

春季批项目阅读

WinCE:连接期间出现 10053 错误

Java_异常处理

春季批处理启动器应用程序 - 无法启动

无法在春季批处理中序列化此事务的访问