【问题标题】:Reset state before each Spring scheduled (@Scheduled) run在每个 Spring 计划(@Scheduled)运行之前重置状态
【发布时间】:2017-01-02 05:11:31
【问题描述】:

我有一个需要每天运行的 Spring Boot Batch 应用程序。它读取每日文件,对其数据进行一些处理,并将处理后的数据写入数据库。在此过程中,应用程序会保存一些状态,例如要读取的文件(存储在FlatFileItemReaderJobParameters中)、当前运行的日期和时间、用于比较读取项的一些文件数据等。

调度的一种选择是使用 Spring 的@Scheduled,例如:

@Scheduled(cron = "${schedule}")
public void runJob() throws Exception {
    jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}

这里的问题是在运行之间保持状态。所以,我要更新要读取的文件,当前运行的日期和时间,清除缓存的文件数据等等。

另一种选择是通过 unix cron 作业运行应用程序。这显然可以满足在运行之间清除状态的需要,但我更喜欢将作业调度与应用程序而不是操作系统联系起来(并且更喜欢它而不是与操作系统无关)。可以在@Scheduled 运行之间重置应用程序状态吗?

【问题讨论】:

  • @Thomas Kåsene,@Hansjoerg Wingeier,感谢您的帮助。该方法有效,但我确实有相当数量的具有单例范围的 bean。我想我可以将它们全部更改为stepprototype 范围。这可能是可能的,但与仅使用 unix cron 相比似乎很麻烦。我还研究了通过在主应用程序类中使用预定的静态 runJob 来刷新上下文。但这无法提供GenericApplicationContext does not support multiple refresh attempts
  • 我已经添加了一个示例,您可以如何处理单例 bean。看看我编辑的例子。

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


【解决方案1】:

Thomas 的方法似乎是一个合理的解决方案,这就是我支持它的原因。缺少的是如何在春季批处理作业的情况下应用它。因此我稍微修改了他的例子:

@Component
public class JobCreatorComponent {


    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Job createJob() {
       // use the jobBuilderFactory to create your job as usual
       return jobBuilderFactory.get() ...
    }
}

您的组件与启动方法 @零件 公共类 ScheduledLauncher {

   @Autowired
   private ... jobRunner;

   @Autwired
   private JobCreatorComponent creator;

@Scheduled(cron = "${schedule}")
public void runJob() throws Exception {

    // it would probably make sense to check the applicationContext and
    // remove any existing job

    creator.createJob(); // this should create a complete new instance of 
                         //  the Job
    jobRunner.runJob(); //runs the batch job by calling jobLauncher.run(job, jobParameters);
}

我还没有尝试过代码,但这是我会尝试的方法。

在构造作业时,重要的是要确保此作业中使用的所有读取器、处理器和写入器也是完整的新实例。这意味着,如果它们没有被实例化为纯 java 对象(不是作为 spring bean)或作为具有范围“step”的 spring bean,您必须确保始终使用新实例。

已编辑: 如何处理 SingletonBeans 有时无法阻止单例 bean,在这些情况下必须有一种方法可以“重置”它们。

一种简单的方法是定义一个接口“ResetableBean”,其中包含由此类 bean 实现的重置方法。然后可以使用 Autowired 收集所有此类 bean 的列表。

@Component
public class ScheduledLauncher {

    @Autowired
    private List<ResetableBean> resetables;

    ...

    @Scheduled(cron = "${schedule}")
    public void runJob() throws Exception {
       // reset all the singletons
       resetables.forEach(bean -> bean.reset());
       ...

【讨论】:

    【解决方案2】:

    您始终可以将执行任务(更重要的是,保持您的状态)的代码移动到原型范围的 bean 中。然后,您可以在每次运行计划的方法时从应用程序上下文中检索该 bean 的新实例。

    示例

    我创建了一个 GitHub 存储库,其中包含我正在谈论的工作示例,但它的要点在于这两个类:

    ScheduledTask.java

    注意@Scope 注释。它指定此组件不应是单例。 randomNumber 字段表示我们希望在每次调用时重置的状态。在这种情况下,“重置”意味着生成了一个新的随机数,只是为了表明它确实发生了变化。

    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    class ScheduledTask {
    
        private double randomNumber = Math.random();
    
        void execute() {
            System.out.printf(
                "Executing task from %s. Random number is %f%n",
                this,
                randomNumber
            );
        }
    }
    

    TaskScheduler.java

    通过在ApplicationContext 中自动装配,您可以在scheduledTask 方法中使用它来检索ScheduledTask 的新实例。

    @Component
    public class TaskScheduler {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Scheduled(cron = "0/5 * * * * *")
        public void scheduleTask() {
            ScheduledTask task = applicationContext.getBean(ScheduledTask.class);
            task.execute();
        }
    }
    

    输出

    运行代码时,下面是一个示例:

    Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask@329c8d3d. Random number is 0.007027
    Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask@3c5b751e. Random number is 0.145520
    Executing task from com.thomaskasene.example.schedule.reset.ScheduledTask@3864e64d. Random number is 0.268644
    

    【讨论】:

    • 感谢您的回答。我会标记为已接受,但 Hansjoerg Wingeier 添加了有关如何在 Spring Batch 应用程序的上下文中使用多个单例 bean 处理此问题的更多信息。我确实赞成您的回答,因为它非常有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多