如何在服务器重启即部署后恢复您在内存中创建的计时器任务及其计时器

问题描述

我正在开发一个应用程序,我们需要在事件的[它的应用程序实体]开始时间的某个特定时间之前处理一些业务逻辑 [事件实体的字段,其中包含事件的开始时间]

我的应用程序接收这些事件并创建计时器任务并将该计时器任务 [用户定义] 分配给计时器,以便 java 可以在给定时间 [事件开始时间前 x 分钟/小时] 执行。

现在,我面临的问题是,如果服务器/spring boot 服务宕机,那么定时器任务和定时器将被破坏,我将无法处理业务逻辑。

我的解决方案(效果不佳):

为了克服这个问题,我尝试了以下逻辑,

  1. 我正在新的符合条件的事件表中添加记录,并且
  2. 在创建计时器任务之前将它们标记为“待处理”
  3. 处理完毕后,我们会将它们标记为“完成”。

使用单个服务器实例: 现在,如果服务器/服务出现故障,我将在新表中记录“待处理”,我可以在服务器启动事件中提取和处理这些记录。这适用于单个服务器实例,但我们在我们的基础架构中有多个服务器实例。

多个服务器实例的问题: 所以主要问题是如何在多个服务器实例中处理这种情况?如何确保只有一个服务器实例将获取记录并执行,因为其他服务器也有可能获取记录并对其进行处理,因此重复计时器任务的机会相同多个服务器实例中的事件,可能会产生我需要避免的问题。

解决方法

您可以为存储在表中的事件使用以下“状态”:

public enum EventState {
    READY,TAKEN,DONE
}

当产生新事件时,您将其添加到表中并将其标记为 READY

当一个线程选择一个事件来处理它时,它会将它标记为 TAKEN 然后开始处理。线程仅选择 READY 事件,没有其他事件。

当线程完成处理事件时,它会将其标记为 DONE(或只是将其从表中删除,这取决于您和您是否需要保留已处理的事件)。>


如果服务崩溃,当您重新启动它时,主线程(在启动任何并行线程之前)将遍历表并检查是否有处于状态 TAKEN 的线程。

如果是这样,则意味着有一个线程已经处理了该事件,但在崩溃之前没有时间完成它。 因此,所有这些 TAKEN 事件都将被重新标记为 READY,以便可以在此镜头中由新线程再次处理。


当然,如果您有多个线程读取表,您应该设置一些锁定机制以确保一次只有一个线程可以更新表。

通常您可以使用 synchronized 方法来做到这一点,例如:

public synchronized Event pickEvent() {
    //Search next READY event in the table
    //Flag it as TAKEN
    //Return it to the thread that should process it
}

上述方法synchronized的事实保证了每次只有一个线程可以进入该方法,这让您不必担心两个线程同时进入并选择同一个事件。

>

Vishal 让我注意到您可能有多个 JVM 读取同一个表。如果是这种情况,synchronized 将无法正常工作,因为它只同步同一 JVM 中的线程。在这种情况下,您将需要在每次有线程进入时锁定表,并在每次有线程退出时解锁。您可以通过创建一个在表中执行读/写操作的小型服务来实现这一点,而不是直接从线程读/写表。