春季批处理调度期间出现的错误
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);
然后在您的控制器中注入所需的JobLauncer
和Job
。需要时构造一个新的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。
【讨论】:
谢谢您的回答和您的笔记以上是关于春季批处理调度期间出现的错误的主要内容,如果未能解决你的问题,请参考以下文章