使用 cron.yaml 安排作业

借助 App Engine Cron 服务,您可以配置在指定时间或按固定时间间隔执行的定期计划任务。这些任务通常称为“Cron 作业”。这些 Cron 作业由 App Engine Cron 服务自动触发。例如,您可以使用 Cron 作业每天发送一封电子邮件报告,每 10 分钟更新一次缓存数据,或者每小时更新一次摘要信息。

Cron 作业使用 HTTP GET 请求调用网址,该请求受到与其他 HTTP 请求相同的限制

免费应用最多可以安排 20 个计划任务。付费应用最多可以安排 250 个计划任务。

如要部署或更新计划,您的帐号需要拥有以下 Identity and Access Management 角色之一:

  • 所有者
  • Editor

您可以在 Google Cloud Console 中的 IAM 页面上设置权限。

cron 配置文件简介

cron.yaml 文件和 app.yaml 文件都位于应用的 WEB-INF 目录下,可为您的 Java 11 应用配置计划任务。下面是一个 cron.yaml 文件示例:

cron:
- description: "daily summary job"
  url: /tasks/summary
  schedule: every 24 hours
- description: "monday morning mailout"
  url: /mail/weekly
  schedule: every monday 09:00
  timezone: Australia/NSW
- description: "new daily summary job"
  url: /tasks/summary
  schedule: every 24 hours
  target: beta

cron.yaml 文件使用 YAML 语法,并且包含每个 Cron 作业的定义。作业定义必须包含 urlschedule。您还可以选择性地指定 descriptiontimezonetargetretry_parameters

url
必需。您希望 Cron 服务将作业请求发送到的应用中的网址。
schedule
必需。定义作业的运行时间表,请参阅下面的语法
description
可选。描述您的 Cron 作业,该作业在 Cloud Console 和本地开发服务器的管理界面中可见。
timezone
可选。您的作业时间表将使用的时区名称或“zoneinfo”。如果您未指定时区,则时间表将使用 UTC(也称为 GMT)。
target
可选。应用中特定服务的名称。如果指定了 target,则 Cron 服务会将作业请求定位到应用中的该服务。作业请求将路由到指定服务中配置为处理流量的版本。了解请求的路由方式。

target 的重要注意事项:

  • 如果启用了流量拆分,则系统不会在您配置的版本之间拆分作业请求:
    • IP 地址拆分:来自 Cron 服务的作业请求始终从同一 IP 地址发送,因此这些请求每次都会路由到同一版本。
    • Cookie 拆分:作业请求不包含 Cookie,因此不会路由到其他任何版本。
  • 如果您使用调度文件,并且 dispatch.yaml 中也配置了同一网址,系统会重新路由您的作业。例如,如果在以下 cron.yamldispatch.yaml 文件中均定义了 /tasks/hello_service2 网址,那么即使指定了 target: service1,作业请求也会被发送到 service2

    cron.yaml

    cron:
    - description: "test dispatch vs target"
      url: /tasks/hello_service2
      schedule: every 1 mins
      target: service1

    dispatch.yaml

    dispatch:
    - url: '*/tasks/hello_service2'
      service: service2
retry_parameters
可选。指定该参数可重新运行失败的作业,请参阅下面的语法

定义 Cron 作业 schedule

Cron 作业按周期性间隔进行安排,并使用类似英文中的简单时间格式指定。您可以定义时间表,以便让您的作业每天运行多次,或在特定的日子和月份运行。

小于一天的时间间隔

使用小于一天的时间间隔,按重复性时间表每天多次运行作业。您可以定义结束时间间隔或开始时间间隔:

  • 结束时间间隔:定义一次作业的“结束时间”与下一次作业的开始时间之间的时间间隔,其中“结束时间”是指作业完成或超时的那个时间点。Cron 服务从 00:00 开始,全天(24 小时)以这种间隔运行作业,并在两个作业之间等待指定的时长。

    示例:对于 every 5 minutes 时间表,作业每天以 5 分钟为间隔运行。如果按此时间表运行的一个作业实例在 02:01 完成,则下一个作业将等待 5 分钟,于 02:06 再次开始运行。

  • 开始时间间隔:定义 Cron 服务启动每个作业的固定时间间隔。与结束时间间隔不同,如果采用开始时间间隔,则每次运行作业时不会考虑前一作业完成或超时的时间。您可以设置要运行作业的时间范围,或者在从 00:00 开始的全天(24 小时)内运行作业。

    由于严格限制了作业的开始时间,因此,如果作业的某个实例的运行时间超过了定义的时间间隔,则 Cron 服务可能会跳过作业。如果前一个作业尚未完成或超时,则系统会跳过间隔中的一个开始时间。

    示例:对于 every 5 minutes from 10:00 to 14:00 时间表,第一个作业在 10:00 开始运行,然后每 5 分钟开始运行下一个作业。如果第一个作业运行 7 分钟,则会跳过 10:05 的作业,因此,Cron 服务直到 10:10 才会运行该作业的另一个实例。

自定义时间间隔

您可以使用自定义时间间隔来定义时间表,让您的作业在一个或多个选定月份中的一个或多个选定日期每天运行一次。按自定义时间表运行的作业会全年运行,而且仅在选定月份和日期的特定时间运行。

示例:对于 1,2,3 of month 07:00 时间表,作业在每月前三天的 07:00 各运行一次。

schedule 的重要注意事项:

  • 您必须决定是使用小于一天的时间间隔还是自定义时间间隔。不能混用各种间隔类型的元素。例如,schedule: every 6 hours mon,wed,fri 这一时间表定义是无效的。
  • 任何时间都只能运行一个作业实例。Cron 服务被设计为提供“至少一次”传送;也就是说,如果安排了某个作业,则 App Engine 会至少发送一次作业请求。在极少数情况下,可能会请求同一作业的多个实例,因此,您的请求处理程序应该具有幂等性,并且您的代码应确保在发生这种情况时不会产生任何有害的副作用。

设置 schedule 的格式

如需指定作业的运行时间,则必须使用以下语法定义 schedule 元素:

schedule: [TYPE] [INTERVAL_VALUE] [INTERVAL_SCOPE]

选择一种间隔类型来定义 schedule 元素:

结束时间间隔
  • [TYPE]:每日间隔必须包含 every 前缀。

    示例:schedule: every 12 hours

  • [INTERVAL_VALUE]:整数值及相应的时间单位。时间单位的有效值包括:
    • minutesmins
    • hours
  • [INTERVAL_SCOPE]:不适用。如需设置作业开始运行的具体时间或运行时间范围,请参阅开始时间间隔自定义时间间隔的语法。
结束时间间隔示例
以下示例可帮助您了解如何定义使用结束时间间隔的作业时间表:
  • 每天 00:00 开始运行,每两个作业之间等待 5 分钟。每次作业结束后,Cron 服务等待 5 分钟后再运行下一次作业:
    schedule: every 5 minutes
  • 每天 00:00 开始运行,每两次作业之间等待 30 分钟。每个作业结束后,Cron 服务等待 30 分钟后再运行下一个作业:
    schedule: every 30 mins
开始时间间隔
  • [TYPE]:每日间隔必须包含 every 前缀。

    示例:schedule: every 12 hours

  • [INTERVAL_VALUE]:整数值及相应的时间单位。时间单位的有效值包括:
    • minutesmins
    • hours
  • [INTERVAL_SCOPE] 指定对应于 [INTERVAL_VALUE] 的子句。您可以自定义时间范围,或使用 24 小时 synchronized 选项。
    • 添加 from [HH:MM] to [HH:MM] 子句,以定义作业开始运行的具体时间和运行时间范围。

      您必须以 24 小时制 (HH:MM) 指定时间值,其中:

      • HH 是从 0023 的整数。
      • MM 是从 0059 的整数。
    • 使用 synchronized 指定 24 小时时间范围 (from 00:00 to 23:59),此范围按 [INTERVAL_VALUE] 值均分。

      重要提示:24 必须能被 [INTERVAL_VALUE] 整除,否则会发生错误。[INTERVAL_VALUE] 的有效值包括:1234681224

开始时间间隔示例
以下示例可帮助您了解如何定义使用开始时间间隔的作业时间表:
  • 每天 10:00 至 14:00,每 5 分钟运行一次:
    schedule: every 5 minutes from 10:00 to 14:00
  • 每天 08:00 至 16:00,每小时运行一次:
    schedule: every 1 hours from 08:00 to 16:00
  • 每天从 00:00 开始,每两小时运行一次:
    schedule: every 2 hours synchronized
自定义时间间隔
  • [TYPE]:自定义时间间隔可以包含 every 前缀以定义重复性间隔,或者您也可以定义一个月中特定日期的列表:
    • 如需定义重复性间隔,您可以使用 every 前缀。

      示例:

      schedule: every day 00:00
      schedule: every monday 09:00

    • 如需定义特定的天数,您必须使用序数。有效值为一个月的第一天到该月的最后一天,例如:
      • 1stfirst
      • 2ndsecond
      • 3rdthird
      • 一直到:31stthirtyfirst

      示例:

      schedule: 1st,3rd tuesday
      schedule: 2nd,third wednesday of month 09:00

  • [INTERVAL_VALUE]:自定义时间间隔包含您希望运行作业的特定天数或星期几的列表。该列表中的值必须用英文逗号隔开,该列表可以包含以下任何一种值:
    • 一个月中日期的整数值,最多 31 天,例如:
      • 1
      • 2
      • 3
      • 一直到:31
    • 混合使用以下任何完整值或简写值的日期名称:
      • mondaymon
      • tuesdaytue
      • wednesdaywed
      • thursdaythu
      • fridayfri
      • saturdaysat
      • sundaysun
      • 使用 day 指定一周中的所有日期。

    示例:

    schedule: 2nd monday,thu
    schedule: 1,8,15,22 of month 09:00
    schedule: 1st mon,wednesday,thu of sep,oct,nov 17:00

  • [INTERVAL_SCOPE]:指定对应于所指定 [INTERVAL_VALUE] 的子句。自定义时间间隔可以包括 of [MONTH] 子句,用于指定一年中的一个月,或以逗号分隔列表的形式指定多个月。您还必须定义希望运行作业的特定时间,例如:of [MONTH] [HH:MM]

    默认情况下,如果不包括 of 子句,则每月均按自定义时间间隔运行。

    • [MONTH]:必须使用英文逗号分隔列表来指定月份,并且可以混用以下完整值或简写值:
      • januaryjan
      • februaryfeb
      • marchmar
      • aprilapr
      • may
      • junejun
      • julyjul
      • augustaug
      • septembersep
      • octoberoct
      • novembernov
      • decemberdec
      • 使用 month 指定一年中的所有月份。
    • [HH:MM]:必须以 24 小时制 (HH:MM) 指定时间值,其中:
      • HH 是从 0023 的整数。
      • MM 是从 0059 的整数。
    • 示例:

      schedule: 1st monday of sep,oct,nov 09:00
      schedule: 1 of jan,april,july,oct 00:00

自定义时间间隔示例
以下示例可帮助您了解如何定义使用自定义时间间隔的作业时间表:
  • 每天 00:00 运行:
    schedule: every day 00:00
  • 每周一 09:00 运行:
    schedule: every monday 09:00
  • 在 3 月第二个星期三的 17:00 运行一次:
    schedule: 2nd wednesday of march 17:00
  • 在 5 月运行六次。在前两周的每周一、周三和周五 10:00 各运行一次:
    schedule: 1st,second mon,wed,fri of may 10:00
  • 每周运行一次。从每个月的第一天开始,每七天在 09:00 运行一次:
    schedule: 1,8,15,22 of month 09:00
  • 每隔一周运行一次。在每个月的第一个和第三个星期一的 04:00 各运行一次:
    schedule: 1st,third monday of month 04:00
  • 每年运行三次。在 9 月、10 月和 11 月第一个星期一的 09:00 各运行一次:
    schedule: 1st monday of sep,oct,nov 09:00
  • 每个季度运行一次。在 1 月、4 月、7 月和 10 月第一天的 00:00 各运行一次:
    schedule: 1 of jan,april,july,oct 00:00

指定重试

如果 Cron 作业的请求处理程序返回的状态代码不在 200-299(含边界值)范围内,则 App Engine 会判定该作业失败。默认情况下,系统不会重试失败的作业。您可以在配置文件中添加 retry_parameters 块,促使系统重试失败的作业。

下面是一个 cron.yaml 文件示例,其中包含一个 Cron 作业,该作业配置为最多重试五次(默认值),初始退避时间为 2.5 秒,每次加倍。

cron:
- description: "retry demo"
  url: /retry
  schedule: every 10 mins
  retry_parameters:
    min_backoff_seconds: 2.5
    max_doublings: 5

Cron 重试语法

下表介绍了重试参数。

元素 说明
job_retry_limit 表示失败的 Cron 作业的重试次数上限的整数。最小值为 0,最大值为 5。如果您还指定 job_age_limit,App Engine 将重试 Cron 作业,直到其同时达到两个限制。job_retry_limit 的默认值为 5
job_age_limit 重试失败的 Cron 作业的时间限制,从首次运行 Cron 作业开始计算。该值是一个数字,后跟一个时间单位,其中 s 表示秒,m 表示分钟,h 表示小时,d 表示天。例如,值 5d 指定的限制为 Cron 作业首次执行尝试后的五天。如果您还指定 job_retry_limit,App Engine 将重试 Cron 作业,直到其同时达到两个限制。
min_backoff_seconds Cron 作业失败后重试该作业之前等待的秒数下限。
max_backoff_seconds Cron 作业失败后重试该作业之前等待的秒数上限。
max_doublings 在增加量变为常量之前,失败的 Cron 作业重试之间的时间间隔将加倍的最大次数。该常量为:2**(max_doublings - 1) * min_backoff

验证 Cron 请求

您可能想要验证对 Cron 网址的请求是来自 App Engine 而不是其他来源。这可以通过验证请求的 HTTP 标头和源 IP 地址来实现:

  • 来自 Cron 服务的请求将包含以下 HTTP 标头:

    X-Appengine-Cron: true
    

    此标头和其他标头由 App Engine 在内部设置。如果客户端发送此类标头,它们会从请求中移除。

  • App Engine 从 IP 地址 0.1.0.2 发出 Cron 请求。对于使用旧版 gcloud(版本低于 326.0.0)创建的 Cron 作业,Cron 请求将来自 0.1.0.1

在 Jetty 或 Tomcat 中,您可以在过滤器中验证 Cron 请求。

上传 Cron 作业

如需上传 Cron 作业,您必须指定 cron.yaml 作为以下 gcloud 命令的参数:

gcloud app deploy cron.yaml

删除 Cron 作业

如需删除所有 Cron 作业,请将 cron.yaml 文件更改为仅包含以下内容:

cron:

Google Cloud Console 中的 Cron 支持

您可以在 Cloud Console 的 Cron 作业页面上查看已安排的 Cron 作业。

您还可以访问“日志”页面,查看 Cron 作业的添加或移除时间。