在我的 Spring Boot 应用程序中,我有一个每分钟运行的计划任务。它运行一些查询来查找任何到期的通知,然后通过电子邮件发送它们
@Service
public class EmailSender {
@Scheduled(cron = "0 * * * * *")
public void runTask() {
// send some emails
}
}
当我在本地测试它时,它工作正常,但是当我将它部署到 Azure 时,每封电子邮件都会发送 2 个副本。这是因为在 Azure 中,我们运行(至少)2 个容器,这两个容器都启用了调度。
我寻找了每个容器的环境变量,调度程序会检查该环境变量,并且仅在该变量设置为“true”时才运行作业,但找不到任何可靠的方法来实现此目的。
我现在正在考虑以下内容
- 删除@Scheduled 注释
- 添加公开任务功能的 HTTP 端点( Controller 方法)
- 使用 Azure Logic Apps 安排每分钟调用此端点(我还考虑过使用 WebJobs ,但 Linux 上的应用服务尚不支持此功能)。
这似乎增加了很多额外的复杂性,我想知道是否有更简单的解决方案?
最佳答案
默认情况下,Spring 无法处理多个实例上的调度程序同步 - 它会在每个节点上同时执行作业。所以有两种可能的解决方案:
使用外部库
您可以使用Spring ShedLock来处理这个问题。这里有一个您可以使用的指南:Spring ShedLock 。您可以在那里找到实现此锁所需的所有代码。
它通过使用数据库中的参数来工作,该参数向其他节点提供有关该任务的执行状态的信息。通过这种方式,ShedLock 可以防止您的计划任务同时执行多次。如果一个任务正在一个节点上执行,它会获取一个锁,以防止从另一个节点执行相同的任务。
您可以在他们的 Github Page 中找到更多信息关于如何使用不同的数据库处理它。
在不同实例中部署 Activity 调度程序
您可以创建两个不同的配置文件,例如,一个是 prod
,另一个是 cron
。然后,您可以将注释 @Profile("cron")
放在调度程序类上,以便它们仅在第二个配置文件上运行。
然后您必须构建应用程序两次:
- 第一次你必须用
-Dspring.profiles.active=prod
并且您可以正常部署它。 - 第二次构建它时
-Dspring.profiles.active=prod,cron
,并且您必须将其部署在 不自动缩放的环境,以便您可以确定 这只是其中一个例子。
关于java - 在部署到 Azure 的 Spring Boot 应用程序中运行计划任务一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68468348/