早上好,我是まさきん。
Cloudflare Workers的官方文档规定,免费计划的Cron Triggers上限是5个。我家里的自动化也是靠免费额度撑着,前几天就碰到了这个上限。在这里结合官方信息,把当时的应对办法整理一下。
※本文为发布时的信息。最新条件请以官方网站为准。
- 在Workers Free中,Cron Triggers整个账号最多5个
- 用途相近的时间可以合并成1个表达式,在处理函数里再做分流
- 只把触发者移到家里的电脑等外部设备,处理本身可以留在Worker里
- 从外部调用时,需要用秘密令牌做认证,并设置好超时
- 如果这样还不够,老老实实升级到付费计划的250个
碰到上限的经过
我给一个推送健康数据的Worker新增了3个Cron Triggers并部署。结果只有日程的设置失败了。错误代码是10072,原因是碰到了Workers Free的账号上限。
代码本身的部署是成功的。所以乍一看像是成功了,这一点有点麻烦。如果不看日志就放着不管,可能就会变成”不知为何就是不运行”。
免费额度的上限是5个
官方的Limits页面中,Cron Triggers的数量是按账号为单位来计算的。重要的是,并不是按每个Worker计算。
| 项目 | Workers Free | Workers Paid |
|---|---|---|
| Cron Triggers数量(整个账号) | 5 | 250 |
| Cron触发时的CPU时间 | 10ms | 30秒(间隔不足1小时)/15分钟(间隔1小时以上) |
| HTTP触发时的CPU时间 | 10ms | 最长5分钟(默认30秒) |
| 请求数 | 每天最多10万(UTC 0点重置) | 每月1,000万以内包含在费用中 |
家里的自动化,往往会按用途拆成一个个小Worker。每个Worker设2〜3个日程,5个很快就用满了。我也是,回过神来已经满了。
复习一下Cron Triggers的基础
在考虑应对办法之前,先确认一下规范。把这些搞清楚,能用的招数就多了。
- 表达式用5个字段(分·时·日·月·星期)来写
- 时间按UTC计算
- 1个Worker可以用数组设置多个日程
- 在scheduled处理函数中,可以用
controller.cron判断是由哪个表达式触发的 - 新增或修改后生效,最多可能需要15分钟左右
需要注意的是UTC这一点。日本时间比UTC快9小时。如果想在早上6点30分运行,用UTC就要写成前一天的21点30分。
controller.cron中存放的是所设置表达式的原样字符串。官方写明,包括空格在内都需要一字不差地匹配。用旧写法event.cron写的代码,思路也是一样的。
应对办法大致有4种
先把我考虑过的选项用表格列出来。
| 应对办法 | 节省额度 | 优点 | 缺点 |
|---|---|---|---|
| (1) 把多个时间合并成1个表达式 | 大 | 不需要额外的机制 | 时间分流要写在代码里 |
| (2) 只把触发者移到外部 | 大 | 可以自由增加频率 | 外部触发者一停就不运行 |
| (3) 集中到调度器Worker | 大 | 在Cloudflare内部完成 | 一处故障会影响全部 |
| (4) 升级到付费计划 | 根本解决 | 上限扩大到250个 | 每月5美元起的费用 |
先试(1),不够的话再进入(2)或(3),最后再考虑(4)。如果要靠免费额度精打细算,这个顺序或许比较顺理成章。
办法1:把时间合并成1个表达式
最省事的就是这个。假设早上和中午分别写了两个表达式。只要在同一个Worker里,把”时”字段用逗号连起来就能合成1个。
我把早上和中午的2个,合并成下面1个后重新部署了。
[triggers]
crons = ["30 3,21 * * *"]
UTC的21点30分,是日本时间早上6点30分。UTC的3点30分,是日本时间中午12点30分。这样就空出了1个额度。
合并成1个表达式后,controller.cron就无法区分是哪个时间了。所以要从预定触发的时间中取出UTC的”时”来分流。
export default {
async scheduled(controller, env, ctx) {
const hourUtc = new Date(controller.scheduledTime).getUTCHours();
if (hourUtc === 21) {
await sendMorning(env); // 日本时间 6:30
} else if (hourUtc === 3) {
await sendNoon(env); // 日本时间 12:30
}
},
};
scheduledTime是预定触发的时间。比起实际运行的时间,用它来判断应该更稳定。
不过,能合并的只限于”分”相同的时间。像6点30分和12点15分这样分钟不一致的,1个表达式就表示不了。这时候,不妨先考虑一下能否把分钟统一。
合并之后还是满的话
我的情况是,即使把早上和中午合并了,5个还是满的,没有余地新增处理。
于是我重新检查了剩下的Cron:位置信息同步1个,家里电子看板更新2个。这3个都是以家里的Mac在线为前提的处理。所以我选择了把触发者移到外部的办法。
家里的自动化说到底都是靠网络通信来运转的。把网络这块打理好,感觉这类机制也能放心地跑起来。
办法2:只把触发者移到外部
思路很简单。处理依然留在Worker里,只把”什么时候运行”交给外部。外部的触发者只需要通过HTTP调用Worker的URL。
我把位置信息同步和电子看板更新的Cron去掉了。改为从家里Mac的launchd调用Worker的URL。这样空出来的额度变成了3个。
还有一个令人惊喜的附带效果。位置信息同步原本每天1次,现在可以从早上8点05分到晚上20点05分每小时执行一次了。不用再操心Cron的额度,这一点很关键。
在Worker端建立入口
在Worker端准备一个用于触发的路径。关键是只放行知道口令的一方。
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname !== "/run/location-sync") {
return new Response("not found", { status: 404 });
}
const auth = request.headers.get("Authorization");
if (auth !== `Bearer ${env.RUN_TOKEN}`) {
return new Response("unauthorized", { status: 401 });
}
await syncLocation(env);
return new Response("ok");
},
};
令牌不要直接写在代码里,而是注册为密钥(Secret)。注册用npx wrangler secret put RUN_TOKEN。代码中可以通过env.RUN_TOKEN读取。
Mac端用launchd来调用
在Mac端,由launchd的任务来执行curl。流程如下。
- 写一个Shell脚本,读取令牌并用curl调用Worker的URL
- 在
~/Library/LaunchAgents中放置plist,指定执行时间 - 用
launchctl加载任务 - 手动执行一次,在Worker端的日志中确认成功
脚本的内容基本上就是一行curl。
curl -fsS --max-time 600 \
-H "Authorization: Bearer ${RUN_TOKEN}" \
https://example.workers.dev/run/location-sync
launchd按Mac的本地时间运行。不需要像Cron Triggers那样换算成UTC,这是个不起眼但很省心的地方。
从外部调用时的注意事项
从外部触发的方式,有一些Cron Triggers所没有的注意事项。把我实际踩过的坑也一起列出来。
只要知道URL,谁都可以调用。请用秘密令牌做认证,并把令牌放在密钥中。curl的超时时间,设得比处理时间长一些会比较安全。
我最头疼的是超时。从Mac调用的处理,把2天的量合在一起大约要73秒。当时curl的超时设的是90秒,结果超时了。现在我把它延长到了600秒。
根据官方说明,通过HTTP触发的Worker,执行时间没有硬性上限。不过有个条件,仅限于客户端保持连接期间。一旦断开,处理有可能中途停止。所以调用方的超时最好留足余量。
另一个是CPU时间。Free每次最多10ms。不过,等待fetch()或数据库响应的时间不计入CPU时间。只是等待外部API的处理,即使看起来耗时很长,很多时候也能控制在范围内。
办法3:集中到调度器Worker
如果多个Worker各自都有Cron,也可以把它们集中起来。只让1个”调度器(Dispatcher)“Worker持有日程。
调度器根据controller.cron或触发时间,调用负责对应任务的Worker。调用目标用HTTP来调用应该最好理解。
这个方法的好处是在Cloudflare内部就能完成。即使家里的电脑在休眠,也能运行。不过,调度器一旦出问题,所有定时处理都会停掉。Free中每次的子请求最多50个,调用目标较多时需要留意。
办法4:升级到付费计划
最直接的是升级到Workers Paid。价格页面上写的是每个账号每月5美元起。
上限从5个扩大到250个。Cron触发时的CPU时间也会延长,较重的处理也更容易放上去。
就我个人而言,家里的自动化大概用不到250个。如果只是额度的问题,先用(1)〜(3)观察一下;如果处理太重而犯难,再考虑付费。我觉得这样的顺序就可以。
我接受的取舍
把触发者移到Mac之后,明确失去了一样东西:电子看板在Mac不运行时就不会更新了。不经过Mac就能送达的途径,已经没有了。
话虽如此,看电子看板都是在家的时候。在家的时候Mac大多也在运行。在这个前提下,我判断是可以接受的。
反过来说,外出时也必须可靠送达的通知,还是留在Cron Triggers里比较放心。从”停了也无所谓的”开始往外移,我觉得这条界线很重要。
常见失误与陷阱
实际做下来,觉得需要注意的地方。
- 忘了用UTC写,结果在相差9小时的时间运行
- 已经把表达式合并成1个,
controller.cron的判断却还保留着旧表达式 - 部署成功了,却没注意到只有日程设置失败了
- 触发用的URL没有加认证,变成谁都能调用的状态
- curl的超时太短,处理中途被断开
- 没有预料到家里电脑在休眠时触发者不会运行
尤其是第3条很容易被忽视。部署后,在控制台或Wrangler的输出中确认日程是否已注册,会比较放心。
适合的人·不适合的人
办法2(把触发者移到外部)适合与否非常分明。
| 内容 | |
|---|---|
| 适合的人 | 有一直在运行的家用电脑或服务器 / 有只需在家时运行的处理 / 想更细致地提高频率 |
| 不适合的人 | 外出或旅行时也需要可靠运行 / 家里电脑经常休眠 / 不想管理认证相关的东西 |
如果属于不适合的一方,(1)或(3)可能更合适。付费计划也是一个选项。
开始前的确认事项
- 已数清目前整个账号用了几个Cron Triggers
- 已确认各个表达式是否用UTC书写
- 已检查同一个Worker中是否有分钟一致、可以合并的表达式
- 已考虑移到外部的候选处理在电脑停机时是否无所谓
- 已准备好给触发用的路径加上秘密令牌认证
- 调用方的超时已设得比处理时间更长
- 已确定部署后确认日程是否注册成功的步骤
常见问题
Q. 5个的上限是按每个Worker计算的吗?
是整个账号5个,不是每个Worker各5个。即使拆分到多个Worker,也是合计计算的。
Q. 超出上限时部署本身会失败吗?
我的情况是代码部署成功了,失败的只有日程设置。错误代码是10072。乍一看像是成功了,所以最好仔细检查输出。
Q. 1个表达式里最多能放几个时间?
把”时”字段用逗号连起来,分钟相同的时间放多少个都行。不过,分钟不同的时间无法用同一个表达式表示。合并得太多,处理函数里的分流会变得难读,这一点也要注意。
Q. controller.cron和event.cron有区别吗?
现在的官方文档是用controller.cron来判断的。controller是scheduled处理函数的第1个参数。旧代码中有时会写成event.cron。两者都是查看触发表达式字符串的思路,是一样的。
Q. 从外部调用的话,免费额度的请求数够用吗?
Free每天最多10万次请求。每小时调用1次的话,一天也就十几次,绰绰有余。不过要记住,它是和其他用途合并计算的。
Q. 除了家用电脑,还有其他方式充当触发者吗?
也可以从外部的定时执行服务,或其他云的定时执行功能来调用。这种情况下,认证和超时的思路也是一样的。用哪个服务,最好根据停掉时的影响来选择。
Q. 升级到付费计划后,CPU时间就不用担心了吗?
Cron触发时的CPU时间,在Paid中会根据间隔延长到30秒或15分钟,比Free的10ms宽裕得多。不过要注意,上限本身并不会消失。
参考的官方信息
- Limits · Cloudflare Workers docs
- Cron Triggers · Cloudflare Workers docs
- Scheduled Handler · Cloudflare Workers docs
- Pricing · Cloudflare Workers docs
- Secrets · Cloudflare Workers docs
总结
Cron Triggers的上限是整个账号5个。持续做家庭自动化的话,会比想象中更早碰到。
我先把早上和中午的表达式合并成了1个。接着,只把以Mac在线为前提的处理的触发者移到了外部。托它的福额度有了余量,位置信息同步也提高到了每小时一次。
作为代价,电子看板变得依赖Mac了。哪些要移到外部,最好根据停掉时会不会造成困扰来决定。
顺带一提,家里电子看板的内容,我写在了让AI读取学校联络本并显示在家里屏幕上的经历里。把早上的信息汇总成一页的机制,整理在了自动生成早间简报的经历里。欢迎一起看看。
趁着重新审视自动化的底层配置,不如也顺便看一眼每月的通信费?
本文包含联盟营销广告。如果您通过本站链接申请相关商品或服务,我们可能会从合作企业获得报酬。此外,运营者是乐天集团的员工,也可能通过员工推荐计划获得报酬。文章内容与评价均基于运营者的真实体验与调查完成,与是否存在广告无关,但请您在阅读前了解上述关系。详情请参阅免责声明与联盟营销说明。 免责声明与联盟营销说明
本文包含机器翻译内容。正式条件请以乐天移动官方网站的多语言页面为准。
如果您对乐天移动的通信区域·信号状况有疑问,可以通过官方的 电波改善调查申请表 进行咨询。