简体   繁体   English

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

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

In my Spring Boot app I have a scheduled task that runs every minute.在我的 Spring Boot 应用程序中,我有一个每分钟运行一次的计划任务。 It runs some queries to find any notifications that are due, and then sends them by email它运行一些查询来查找任何到期的通知,然后通过电子邮件发送它们

@Service
public class EmailSender {

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

It works fine when I test it locally, but when I deploy it to Azure, 2 copies of each email are sent.当我在本地测试它时它工作正常,但是当我将它部署到 Azure 时,每封电子邮件都会发送 2 个副本。 This is because in Azure we run (at least) 2 containers, both of which have the scheduling enabled.这是因为在 Azure 中我们运行(至少)2 个容器,它们都启用了调度。

I looked for a per-container environment variable, which the scheduler would check, and only run the job if this variable is set to "true", but couldn't find any reliable way to achieve this.我寻找了一个每个容器的环境变量,调度程序会检查它,并且只有在此变量设置为“true”时才运行作业,但找不到任何可靠的方法来实现这一点。

I'm now considering the following instead我现在正在考虑以下内容

  • Removing the @Scheduled annotation删除@Scheduled 注释
  • Adding a HTTP endpoint (controller method) that exposes the functionality of the task添加暴露任务功能的 HTTP 端点(控制器方法)
  • Scheduling this endpoint to be called every minute using Azure Logic Apps (I also considered using WebJobs , but this is not yet supported for App Service on Linux).使用Azure Logic Apps安排此端点每分钟调用一次(我也考虑使用WebJobs ,但 Linux 上的应用服务尚不支持此功能)。

This seems like a lot of additional complexity, and am wondering if there's a simpler solution?这似乎增加了很多额外的复杂性,我想知道是否有更简单的解决方案?

Spring, by default, cannot handle scheduler synchronization over multiple instances – it executes the jobs simultaneously on every node instead.默认情况下,Spring 无法处理多个实例上的调度程序同步——而是在每个节点上同时执行作业。 So there are two possible solutions:所以有两种可能的解决方案:

Using an external library使用外部库

You can use Spring ShedLock to handle this though.不过,您可以使用Spring ShedLock来处理此问题。 Here there is a guide you can use: Spring ShedLock .这里有一个您可以使用的指南: Spring ShedLock You can find all the code need to implement this lock there.您可以在那里找到实现此锁所需的所有代码。

It works by using a parameter in a database, that gives the information to the other nodes about the execution status of that task.它通过使用数据库中的参数来工作,该参数将有关该任务执行状态的信息提供给其他节点。 In this way ShedLock prevents your scheduled tasks from being executed more than once at the same time.通过这种方式 ShedLock 可以防止您的计划任务同时执行多次。 If a task is being executed on one node, it acquires a lock which prevents the execution of the same task from another node.如果一个任务正在一个节点上执行,它会获取一个锁,以防止从另一个节点执行相同的任务。

You can find more info in their Github Page about how to handle it with different databases.您可以在他们的Github 页面中找到有关如何使用不同数据库处理它的更多信息。

Deploying the active schedulers in a different instance在不同的实例中部署活动调度程序

You can create two different profiles, for example, one is prod and the other is cron .您可以创建两个不同的配置文件,例如,一个是prod ,另一个是cron Then you can put the annotation @Profile("cron") on scheduler classes, so that they will be ran only on the second profile.然后,您可以将注释@Profile("cron")放在调度程序类上,以便它们仅在第二个配置文件上运行。

Then you have to build the application twice:然后你必须构建应用程序两次:

  1. The first time you have to build it with -Dspring.profiles.active=prod and you deploy it normally.第一次必须使用-Dspring.profiles.active=prod构建它并正常部署它。
  2. The second time you build it with -Dspring.profiles.active=prod,cron , and you have to deploy this in an environment that is not autoscaling so that you can be sure there will be only one instance of this.第二次使用-Dspring.profiles.active=prod,cron构建它时,您必须将其部署在非自动缩放的环境中,以便确保只有一个实例。

Option 4选项 4

(Option 3 being the one in the comment by @MDeinum) (选项 3 是@MDeinum 评论中的选项)

Factor your code so the executable job runs against a REST request, protected by proper authentication (a GUID parameter is sufficient for the purpose, feel free to use more robust schemes).分解您的代码,以便可执行作业针对 REST 请求运行,并受到适当身份验证的保护(GUID 参数足以满足此目的,请随意使用更强大的方案)。 Disable scheduling offered by @Scheduled annotation.禁用@Scheduled注释提供的调度。 Use an Azure Automation account to periodically invoke the REST API.使用 Azure 自动化帐户定期调用 REST API。

The idea is that the REST invocation is routed to exactly one node, so only one node is awakened.这个想法是 REST 调用被路由到一个节点,所以只有一个节点被唤醒。

The drawback is in security, as you must expose an API that just runs a job , ie.缺点是安全性,因为您必须公开一个仅运行作业的 API,即。 can be run repeatedly, and can be potentially abused.可以重复运行,并且可能被滥用。 Even if it's not exposed to the internet, someone may repeatedly curl to that API using the valid developer's token.即使它不暴露于互联网,有人可能会反复curl使用有效的开发者令牌是API。

I am comfortable with this approach because at some point I will be required to implement a "force run scheduled task" feature to be used when a previous execution of the job has failed.我对这种方法很满意,因为在某些时候我将需要实现一个“强制运行计划任务”功能,以便在先前的作业执行失败时使用。

Personally, I had to deliver this kind of request on every single project I participated.就我个人而言,我必须对我参与的每个项目都提出这种要求。

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

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