【问题标题】: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

标签: spring spring-boot spring-integration spring-batch spring-scheduled


【解决方案1】:

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

  1. 不要在控制器中重新创建ThreadLocal。定义一次,然后保持原样。
  2. 您的JobThread 是一个保持状态(参数)的单例,因此只保留最后一个。
  3. 编程接口 TaskScheduler 而不是具体实现
  4. 不要创建线程,因为您的 JobThread 已经是 Runnable
  5. 不要将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

【讨论】:

  • 谢谢您的回答和您的笔记
猜你喜欢
  • 2019-05-15
  • 2018-04-03
  • 2014-12-15
  • 2018-05-27
  • 2015-05-13
  • 2017-06-25
  • 2017-10-06
  • 1970-01-01
  • 2021-06-21
相关资源
最近更新 更多