我们近期正在根据收集到的反馈对任务队列功能进行改进和重新设计,这个帖子描述的内容可能是过时的,请关注任务队列最新的消息。
「任务队列」是一种在云引擎之外调度云函数的能力,目前仅限云引擎内部使用 masterKey 调用,提供了一种在云引擎之外调度云函数的能力,任务队列会保证即使云引擎的进程重启、退出、出错,任务最终都会成功执行(除非超过了重试次数)。
任务队列基于云引擎已有的「云函数」这个概念实现了延时任务和重试等功能,是对云函数功能的一个补充,可以实现很多用户一直想要的「过十分钟后运行一个云函数来关闭订单」或者「保证云函数在运行失败时能够重试」这样的功能。
API
目前我们只在 Node SDK(3.2 以上版本)添加了实验性的任务队列支持:
AV.Cloud.enqueue(functionName, params, options?): Promise<TaskInfo>
用法:
// 添加任务,enqueue 本身在添加队列成功就返回(不等待任务实际被执行)
AV.Cloud.enqueue('sendMail', {userId: 1234};
// 延时任务
AV.Cloud.enqueue('closeOrder', {id: 1234}, {delay: 600000});
options
的属性包括:
-
attempts?: number
:最大重试次数,默认 3
.
-
backoff?: number
:重试间隔(毫秒),默认 60000
.
-
delay?: number
:延时执行(毫秒)。
-
notify?: string
:将执行结果通知到指定云函数。
-
retryTimeout?: boolean
:将超时视作失败来进行重试,默认 true
,如果这个任务宁可失败也不应该重试,请设置为 false
.
TaskInfo
的属性包括:
-
uniqueId
:任务的唯一 Id,会包含在日志中。
下面我们来具体介绍一下任务队列的使用场景:
延时任务
// 被运行的云函数
AV.Cloud.define('delayTask', function() {
console.log('running delayTask');
});
// 延时运行云任务,将在 5 秒之后执行
AV.Cloud.enqueue('delayTask', {}, {
delay: 5000
});
不同于在 Node.js 中自行用 setTimeout
设置延时,任务队列可以保证即使进程重启也会按时执行。
完整代码见 cloud-queue.js。
重试
// 被运行的云函数
AV.Cloud.define('retryTask', function() {
var random = Math.random();
if (random >= 0.5) {
console.log('running retryTask: success');
} else {
console.log(`running retryTask failed: ${random}`);
throw new AV.Cloud.Error(`failed: ${random}`);
}
});
// 重试任务,每隔 2 秒重试,最多 5 次
AV.Cloud.enqueue('retryTask', {}, {
attempts: 5,
backoff: 2000
});
不同于在 Node.js 内自行重试,任务队列可以保证即使进程重启也会继续重试。如果不指定这两个选项,任务队列会默认以 1 分钟的间隔重试 3 次。
完整代码见 cloud-queue.js。
爬虫
完整代码见 crawler.js。
在这个例子中我们实现了一个简单的爬虫来抓取 LeanCloud 的文档页面,云函数 crawling 是抓取的主要逻辑,它会将结果保存到云存储(CrawlerResults)中、并继续将页面中的链接通过 queuePage 函数来加入队列,queuePage 会用 Redis 来保证抓取的唯一性,避免反复抓取同一页面。
这样每次抓取页面都是一次云函数调用,即使要抓取的页面的量非常大也不会有超时或中断的问题,任务队列会控制云函数的并发执行数量(目前是控制在 10),避免同时发出太多的抓取请求。如果发生了意外的错误,队列还会进行三次重试。
长时间运行的单个任务
完整代码见 long-running.js。
这个示例模拟了这样一个场景,客户端提交了一个耗时较长的计算任务,我们将其加入任务队列来运行,为了让客户端能够查询任务的执行情况,我们为每个任务在云存储的 Tasks 中创建了一个对象,客户端可以根据 ID 来查询这个任务的执行进度和结果。
即使在运行期间云引擎发生重启或其他故障,任务队列也会将其重试,并且在云存储中有记录可查。
性能和可靠性
任务队列的入队接口(Cloud.enqueue
)被设计用于应对较高的突发流量(例如 1000 QPS 以上),任务队列会将这些任务存储起来,以 10 个并发的速度逐步地执行,减少对于云引擎容器的压力。
任务队列的调度并非在云引擎容器中进行,这意味着即使云引擎容器故障、重启也不会影响到任务队列(任务不会丢失,失败的任务会重试),可以用于支持突发流量。同时任务本身又是以云函数的形式在云引擎容器中运行的(占用云引擎的 CPU、内存资源),和直接调用云函数的执行环境完全相同。
更多细节
目前任务队列的执行超时和云函数一样都是 15 秒,保持这个限制是为了尽可能减少与云函数的不一致。确实 15 秒对于一些复杂任务来说显得有些短,目前任务队列还属于实验性功能,我们会继续关注这个限制的合理性,也欢迎大家反馈具体的使用场景和建议。
在任务队列功能正式上线后将会是一项单独计费的功能,因为任务队列被设计用于应对突发流量,所以收费的指标将会与「每小时入队任务数量(对应队列的处理能力)」和「队列内剩余任务峰值(对应队列空间的占用)」相关,按实际用量计费,不需要预估容量。