在部署到 Azure 的 Spring Boot 应用程序中运行计划任务一次

Ant*_*gos 4 java spring scheduled-tasks azure spring-boot

在我的 Spring Boot 应用程序中,我有一个每分钟运行的计划任务。它运行一些查询来查找到期的通知,然后通过电子邮件发送它们

@Service
public class EmailSender {

    @Scheduled(cron = "0 * * * * *")
    public void runTask() {
      // send some emails
    }
}
Run Code Online (Sandbox Code Playgroud)

当我在本地测试时它工作正常,但当我将其部署到 Azure 时,每封电子邮件都会发送 2 个副本。这是因为在 Azure 中,我们运行(至少)2 个容器,这两个容器都启用了调度。

我寻找了每个容器的环境变量,调度程序将检查该环境变量,并且仅在该变量设置为“true”时才运行作业,但找不到任何可靠的方法来实现此目的。

我现在正在考虑以下问题

  • 删除@Scheduled注释
  • 添加公开任务功能的 HTTP 端点(控制器方法)
  • 使用Azure 逻辑应用安排此端点每分钟调用一次(我也考虑过使用WebJobs,但 Linux 上的应用服务尚不支持此功能)。

这似乎增加了很多额外的复杂性,我想知道是否有更简单的解决方案?

rob*_*tts 5

默认情况下,Spring 无法处理多个实例 \xe2\x80\x93 上的调度程序同步,而是在每个节点上同时执行作业。所以有两种可能的解决方案:

\n

使用外部库

\n

不过,您可以使用Spring ShedLock来处理这个问题。这里有一个您可以使用的指南:Spring ShedLock。您可以在那里找到实现此锁所需的所有代码。

\n

它通过使用数据库中的参数来工作,该参数向其他节点提供有关该任务的执行状态的信息。通过这种方式,ShedLock 可以防止您的计划任务同时执行多次。如果一个任务正在一个节点上执行,它会获取一个锁,以防止从另一个节点执行同一任务。

\n

您可以在他们的Github 页面中找到有关如何使用不同数据库处理它的更多信息。

\n

在不同的实例中部署活动调度程序

\n

您可以创建两个不同的配置文件,例如,一个是prod,另一个是cron。然后,您可以将注释放在@Profile("cron")调度程序类上,以便它们仅在第二个配置文件上运行。

\n

然后你必须构建应用程序两次:

\n
    \n
  1. 第一次您必须使用\n 构建它-Dspring.profiles.active=prod并正常部署它。
  2. \n
  3. 第二次使用\n构建它时-Dspring.profiles.active=prod,cron,您必须将其部署在\n不自动缩放的环境中,以便您可以确保\n只有一个实例。
  4. \n
\n

  • 选项 3,使用quartz 和数据库驱动的调度程序,在单个实例上执行。 (3认同)