繁体   English   中英

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

[英]Run scheduled task once within Spring Boot app deployed to Azure

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

@Service
public class EmailSender {

    @Scheduled(cron = "0 * * * * *")
    public void runTask() {
      // send some emails
    }
}

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

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

我现在正在考虑以下内容

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

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

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

使用外部库

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

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

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

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

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

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

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

选项 4

(选项 3 是@MDeinum 评论中的选项)

分解您的代码,以便可执行作业针对 REST 请求运行,并受到适当身份验证的保护(GUID 参数足以满足此目的,请随意使用更强大的方案)。 禁用@Scheduled注释提供的调度。 使用 Azure 自动化帐户定期调用 REST API。

这个想法是 REST 调用被路由到一个节点,所以只有一个节点被唤醒。

缺点是安全性,因为您必须公开一个仅运行作业的 API,即。 可以重复运行,并且可能被滥用。 即使它不暴露于互联网,有人可能会反复curl使用有效的开发者令牌是API。

我对这种方法很满意,因为在某些时候我将需要实现一个“强制运行计划任务”功能,以便在先前的作业执行失败时使用。

就我个人而言,我必须对我参与的每个项目都提出这种要求。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM