[Node] Agenda 中文文档 定时任务调度系统[基础篇]
Agenda简介
Agenda是一个定时任务管理模块,它将node中的定时任务存储在数据库中(官方文档推荐使用mongoDB),通过事件回调与监听实现定时任务调度。
使用步骤概述
总的来说分4步:
- 初始化(
new Agenda(..)
) - 定义任务(
agenda.define(..)
) - 配置任务(
agenda.every(..)
) - 最后设置事件监听(
agenda.on(..)
const Agenda = require('agenda')
const connection_options = {
db: {
address: 'mongodb://127.0.0.1:27017/agenda',
collection: 'agendaJobs',
options: { server: { auto_reconnect: true } },
},
}
// 初始化agenda
let agenda = new Agenda(connection_options)
agenda
.name('AGENDA TEST - ' + process.pid)
.defaultConcurrency(5)
.defaultLockLifetime(10000)
// 定义任务
agenda.define('updateCampaignTimeout', { priority: 'high', concurrency: 3 }, (job, done) => {
dbp_job.updateCampaignTimeoutTick()
.then(() => done())
.catch((error) => { throw error })
})
// 配置任务(需要在ready事件中完成)
agenda.on('ready', () => {
agenda.every('00 09 18 * * *', 'updateCampaignTimeout', {}, {timezone: 'Asia/Shanghai'})
console.log('agenda测试开始,启动完毕')
agenda.start()
})
// 设置监听
agenda.on('start', (job) => {
console.log('检测到job启动: ', job.attrs.name)
})
agenda.on('complete', (job) => {
console.log('检测到job完成: ', job.attrs.name)
})
agenda.on('success', (job) => {
console.log('检测到job成功: ', job.attrs.name)
})
agenda.on('fail', (job) => {
console.log('检测到job失败: ', job.attrs.name)
console.log('失败时间: ', job.attrs.failedAt)
console.log('失败原因: ', job.attrs.failReason)
agenda.stop()
})
// 最后,优雅的退出方案
function graceful() {
agenda.stop(() => {
console.log('检测到退出')
process.exit(0);
});
}
process.on('SIGTERM', graceful);
process.on('SIGINT', graceful);
步骤详述
初始化
初始化有两种方案:
- 通过完整的参数传递
- 链式初始化(推荐使用)
部分说明:
.database(url, [collectionName])
连接数据库,例如agenda.database('mongodb://127.0.0.1:27017/agenda', 'agendaJobs')
,其中collectionName
可省略,默认值为agendaJobs
.name(string)
jobs存储于数据库中会有lastModifiedBy
字段,存储调度这个任务的agenda名字.processEvery(interval)
用于设置agenda轮询数据库检测是否有新的任务加入的扫描时间.defaultLockLimit(number)
设置默认值,agenda调度任务时会锁定该任务,防止其他agenda重复执行任务,当任务超出lockLifetime或调用done时会解锁任务,define新任务时如果设置.lockLimit(number)
则会覆盖默认值。该值用于设置锁定任务的数量,默认为0,即无限制.defaultLockLifetime(number)
上面说了,当任务被调度时会被锁定,当任务调用完毕执行done()
后会解锁,或者执行的时间很长,超出了lockLifetime也会解锁,同样,define其他任务时如果设置了lockLifetime参数,会覆盖掉默认值
定义任务
agenda.define(jobName, [options], fn)
agenda.define('updateCampaignTimeout', { priority: 'high', concurrency: 3 }, (job, done) => {
dbp_job.updateCampaignTimeoutTick()
.then(() => done())
.catch((error) => { throw error })
})
参数说明:
jobName
类型String
,用于存储于数据库中的name
字段,同时配置任务和设置监听会使用到该参数
options
可配置concurrency
、lockLimit
、lockLifetime
、priority
,前文都已说过,
priority
为任务优先级:lowest|low|normal|high|highest|number
,前五个是字符串,传入后会被转为数字,依次对应:-20|-10|0|10|20
fn(job, done)
:
* 配置任务时,会设置每次执行任务需要传入的参数,该参数都被放在了job.attrs.data
中,
* 任务出错,会调用job.fail(reason)
方法,其中的reason
可以是字符串或Error
,当调用此方法时,会设置job.attrs.failedAt
为当前时间(ISO),job.attrs.failReason
为reason
或Error.message
* 对于异步任务,必须调用done
方法告知任务结束
* 对于同步任务,省略为fn(job)
即可
配置任务
agenda.every(interval, name, [data], [options], [cb])
agenda.every('00 09 18 * * *', 'updateCampaignTimeout', {}, {timezone: 'Asia/Shanghai'})
agenda.every('30 seconds', 'updateCampaignOverBudget')
agenda.every('1 minutes', 'synchronizeBudget')
参数说明
interval
可以是字符串,或cron时间,注意:官方Agenda文档说明的cron时间格式已过时,现在的cron时间格式为
* * * * * *
| | | | | |
| | | | | +-- Day of the Week (range: 0-6, 0 standing for Monday)
| | | | +---- Month (range: 0-11)
| | | +------ Day of the Month (range: 1-31)
| | +-------- Hour (range: 0-23)
| +---------- Minute (range: 0-59)
+------------ Second (range: 0-59)
设置每隔几分钟的任务用'5 minutes'
就好了,但是如果设置每天的什么时候运行,就要使用cron设置,且要注意时区(在options中设置)!!
name
就是任务的名字,一定要对应,可以是字符串数组,这样的话多个任务就使用同一个配置
data
是一个Object
,在define
任务时通过job.attrs.data
获取该对象
options
注意,要设置options
必须先设置data
,所以data可以传空Object{}
,在options
中可以设置的有timezone
,必须是可识别的时区字符串,默认为'America/New_York
,而在我们的时区,必须要设置对时区,才能正常运行定时任务!我们的时区字符串是Asia/Shanghai
cb
当job
成功被加入数据库时会调用回调函数
设置监听
agenda.on('ready', () => {
// agenda.every('5 seconds', 'say hello')
agenda.every('00 09 18 * * *', 'updateCampaignTimeout', {}, {timezone: 'Asia/Shanghai'})
agenda.every('30 seconds', 'updateCampaignOverBudget')
agenda.every('1 minutes', 'synchronizeBudget')
agenda.purge((err, numRemoved) => {
console.log('旧任务被移除: ', numRemoved)
})
console.log('agenda测试开始,启动完毕')
agenda.start()
})
agenda.on('fail', (job) => {
console.log('检测到job失败: ', job.attrs.name)
console.log('失败时间: ', job.attrs.failedAt)
console.log('失败原因: ', job.attrs.failReason)
agenda.stop()
})
可以监听的事件有:
'ready', function()
'start', function(job)
'start:job name', function(job)
'complete', function(job)
'complete:job name', function(job)
'success', function(job)
'success:job name', function(job)
'fail', function(err, job)
'fail: job name', function(err, job)
其中比较重要的事件就是ready
了,当agenda初始化完毕成功连接数据库后,会发出ready
事件,此时就要通过监听该事件,在ready
时间中对define
过的任务配置时间等参数,最后调用agenda.start()
启动agenda
我们发现,在ready
事件中,我们还调用了agenda.purge(cb)
,这个函数是清理数据库中的旧任务(即这此运行程序没有被define到的job)
注意事项
- agenda在mongoDB中的存储方式:
{
"_id" : ObjectId("5981983d9b85994179fc98c9"),
"name" : "updateCampaignTimeout",
"type" : "single",
"data" : {
},
"priority" : 10,
"repeatInterval" : "00 09 18 * * *",
"repeatTimezone" : "Asia/Shanghai",
"lastModifiedBy" : "AGENDA TEST - 97807",
"nextRunAt" : ISODate("2017-08-03T10:09:00.011Z"),
"lockedAt" : null,
"lastRunAt" : ISODate("2017-08-02T10:09:00.011Z"),
"lastFinishedAt" : ISODate("2017-08-02T10:09:00.076Z"),
"failReason" : "failed to calculate nextRunAt due to invalid repeat interval",
"failCount" : 1,
"failedAt" : ISODate("2017-08-02T09:44:54.551Z")
}
重复启动agenda时,会更新对应的项,而不是覆盖,只要任务名相同,就不会在数据库中创建新的数据项,也不会删除旧的job(该次执行程序没有define到的job),除非在ready事件中调用
agenda.purge
在配置任务(
agenda.every
)时一定要注意时区的设置agenda.every
配置失败时(传入的interval不对等原因),会返回undefined
,正常情况下返回job
对象关于job对象:
Job {
agenda:
EventEmitter {
_name: 'AGENDA TEST - 97807',
_processEvery: 5000,
_defaultConcurrency: 5,
_maxConcurrency: 20,
_defaultLockLimit: 0,
_lockLimit: 0,
_definitions:
{ updateCampaignTimeout: [Object],
updateCampaignOverBudget: [Object],
synchronizeBudget: [Object] },
_runningJobs: [],
_lockedJobs: [],
_jobQueue: [],
_defaultLockLifetime: 10000,
_isLockingOnTheFly: false,
_jobsToLock: [],
_events:
{ ready: [Function],
start: [Function],
complete: [Function],
success: [Function],
fail: [Function] },
_eventsCount: 5,
_mdb:
Db {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
s: [Object],
serverConfig: [Getter],
bufferMaxEntries: [Getter],
databaseName: [Getter] },
_collection: Collection { s: [Object] } },
attrs:
{ name: 'updateCampaignTimeout',
data: {},
type: 'single',
priority: 10,
nextRunAt: moment.parseZone("2017-08-02T18:09:00.009+08:00"),
repeatInterval: '00 09 18 * * *',
repeatTimezone: 'Asia/Shanghai' } }
- 官方README的CRON格式过时了,最新的CRON格式请见上文
待补充……
[Node] Agenda 中文文档 定时任务调度系统[基础篇]的更多相关文章
- axios 中文文档(转载)
axios中文文档 转载来源:https://www.jianshu.com/p/7a9fbcbb1114 原始出处:lewis1990@amoy axios 基于promise用于浏览器和node. ...
- Apache Spark 2.2.0 中文文档
Apache Spark 2.2.0 中文文档 - 快速入门 | ApacheCN Geekhoo 关注 2017.09.20 13:55* 字数 2062 阅读 13评论 0喜欢 1 快速入门 使用 ...
- 分布式定时任务调度系统技术解决方案(xxl-job、Elastic-job、Saturn)
1.业务场景 保险人管系统每月工资结算,平安有150万代理人,如何快速的进行工资结算(数据运算型) 保险短信开门红/电商双十一 1000w+短信发送(短时汇聚型) 工作中业务场景非常多,所涉及到的场景 ...
- Solidity 最新 0.5.8 中文文档发布
本文首发于深入浅出区块链社区 热烈祝贺 Solidity 最新 0.5.8 中文文档发布, 这不单是一份 Solidity 速查手册,更是一份深入以太坊智能合约开发宝典. 翻译说明 Solidity ...
- Flutter 中文文档网站 flutter.cn 正式发布!
在通常的对 Flutter 介绍中,最耳熟能详的是下面四个特点: 精美 (Beautiful):充分的赋予和发挥设计师的创造力和想象力,让你真正掌控屏幕上的每一个像素. ** 极速 (Fast)**: ...
- ENS中文文档系列之一 [ ENS介绍 ]
前言 ENS中文文档是由我照ENS英文官方文档翻译而来,其中的一些内容和细节得到了ENS官方团队的指导.文档中包含 “LBB译注” 的地方是译者为了便于读者理解而进行的注释. 未来一段时间,我会在该博 ...
- Akka Java 中文文档
Akka Java 中文文档 Introduction What is Akka? | 什么是Akka? Why Akka? | 为什么选择Akka? Getting Started | Akka入门 ...
- 一、neo4j中文文档-入门指南
目录 neo4j中文文档-入门指南 Neo4j v4.4 neo4j **Cypher ** 开始使用 Neo4j 1. 安装 Neo4j 2. 文档 图数据库概念 1. 示例图 2.节点 3. 节点 ...
- Phoenix综述(史上最全Phoenix中文文档)
个人主页:http://www.linbingdong.com 简书地址:http://www.jianshu.com/users/6cb45a00b49c/latest_articles 网上关于P ...
随机推荐
- 【题解】洛谷P1006传纸条
链接 https://www.luogu.org/problemnew/show/P1006 日常牢骚 过年前最后一节课上完了坐标DP 也接触了一点区间DP(noi1995石子合并)下次做做看看吧 老 ...
- 消息中间件JMS(一)
1.JMS入门 1.1消息中间件 模块之间的依赖也称之为耦合.而耦合越多,之后的维护工作就越困难.那么如果改善系统模块调用关系.减少模块之间的耦合呢?我们接下来就介绍一种解决方案----消息中间件. ...
- JavaScript函数变量作用域
变量作用域 在JavaScript中,用var申明的变量实际上是有作用域的. 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量. 如果两个不同的函数各自申明了同一变 ...
- 与select2有关的知识点总结
1.多选下拉框设置提示 var datass = [ { id:0, text: '你好' }, { id:1, text: '好久不见' }, { id:2, text: '好想你' } ]; va ...
- Python 学习笔记(八)Python列表(一)
列表基本操作 列表(list)定义 列表是Python中的一种对象类型,也是一种序列 对象类型:list 表示方法:[ ] python 列表中的元素可以是任何类型的对象 >>> ...
- MySQL的数据类型(一)
每一个常量.变量和参数都有数据类型.它用来指定一定的存储格式.约束和有效范围.MySQL提供了多种数据类型.主要有数值型.字符串类型.日期和时间类型.不同的MySQL版本支持的数据类型可能会稍有不同. ...
- vue.js中的slot
vue.js 中的 slot 一.slot 的作用 调用组件的时候,对于数据,我们会用props将数据从父组件传至子组件.但是,如果从父组件到子组件,单纯是页面局部渲染的改变,slot会更合适. 二. ...
- 【TOJ 3005】Triangle(判断点是否在三角形内+卡精度)
描述 Given the coordinates of the vertices of a triangle,And a point. You just need to judge whether t ...
- VMware下CentOS7安装后,还原虚拟网络后,敲ifconfig不显示局域网ip解决方法
VMware下CentOS7安装后,还原虚拟网络后,敲ifconfig不显示局域网ip,没有出现eth0网卡,不能上网,SSH不能连接,输入ifconfig后如下图: 解决方法: 1.编辑网卡的配置文 ...
- python实现归并排序,归并排序的详细分析
python实现归并排序,归并排序的详细分析. 学习归并排序的过程是十分痛苦的.它并不常用,看起来时间复杂度好像是几种排序中最低的,比快排的时间复杂度还要低,但是它的执行速度不是最快的.很多朋友不 ...