1

在软件系统的运维中,总有一些事件,需要在特定的时间来触发执行,这个时候,我们需要用到定时任务。

agenda是nodejs实现的基于mongodb数据库的分布式定时任务管理系统。agendash则为agenda提供了一个web管理控制台。

这篇文章,通过一个demo项目,演示了如何使用agenda来管理定时任务。

为什么需要管理定时任务

最早用crontab管理来管理定时任务的时候,需要手动去服务器上设置任务,然后还要在服务器上部署定时执行的程序。整个过程依赖手工操作,用起来感觉特别不踏实。那时候在想,应该有一种更方便的管理定时任务的机制,所以尽量在设计上减少定时任务的使用,只在最必要的时候才会使用。

后来看了很多技术分享,看到知乎的分享他们的运维系统的经验,实现了一个可以配置,并且有精美的管理后台的定时任务管理系统,能够在运维后台方便的对任务进行操作。

现在已经可以简单的使用agendash和agenda集成,就方便了实现了任务管理,从而可以更专注的实现业务逻辑

实现原理

虽然不需要自己再去发明轮子是非常方便的事情,但是还是应该试着去自己动手做一些尝试,实际实现起来成本比较高,但是思考一下如何实现,还是有价值的。

agenda通过mongodb实现了任务在定时任务处理集群中的共享和锁定,从而实现了分布式执行定时任务的功能,实现了更高的可用性;

再结合作为express插件开发的agendash,我们就可以通过管理后台查询和管理定时任务。

实现思路

如果我自己实现的话,首先要考虑定时任务如何设置,就是语法,agenda并没有自己发明轮子,而是使用了已有的cron模块,该模块的定时任务配置语法,和crontab一样,是形如*/5 * * * *的形式。

我们要考虑的是定时任务的机制,程序执行之后,一直在进程中轮询,是否符合触发条件,如果符合触发条件,就会触发设置的要执行的业务逻辑相关代码,crontab实际上也是触发执行shell脚本的代码。最后由业务逻辑代码根据自己的实际情况运行。

任务的调用,可以用观察者模式实现。在nodejs中,可以方便的使用回调函数,将任务的名字和回调函数绑定,这样,任务条件达到的时候,就会触发执行回调函数;可以结合nodejs的queue队列模块来实现。

只有把任务用专有的数据结构存储起来,才有可能实现某种任务本身的逻辑,需要什么样的逻辑,就需要什么样的数据结构做支撑。因此mongodb中要存储的数据包括:执行条件、任务名称、创建时间、上次开始时间、上次完成时间、下次执行时间、目前是否在锁定状态。

这样,某个节点执行是,正好符合条件,它先设置任务状态为锁定,然后调用回调函数,完成之后,在解除锁定,并且设置相应的时间;如果出现程序意外崩溃,其他节点检查是否超过一定处理时间,会将任务的锁定状态解除,然后接下来的其他节点,就会检查并且执行任务。

使用agenda和agendash

agenda本身运行,是不需要web服务器;要使用agendash,目前必须安装express,将agendash作为插件添加到express中,就可以正常的访问。

演示程序的具体步骤如下:

  1. 安装依赖包

  2. 搭建express脚手架

  3. 添加agenda任务和任务处理代码

  4. 编写简单的测试

  5. 添加集成相关配置

安装依赖包

项目需要用到的依赖包分为三类,基本业务逻辑需要,es6编译相关、还有测试依赖包。以下命令不包含babel相关依赖,请参看其他网上的教程。

npm install --saveagenda agendash express mongoose ejs

npm install --save-dev mocha chai supertest

搭建express脚手架和agendash

import path from 'path'
import './config'
import {agendash } from './middlewares'

var express = require('express');
var app = express();
var router = express.Router();

app.set("view engine","ejs"); 
app.set('views', './views')
app.set("view options",{     "open":"{{",     "close":"}}"  });  

app.use(express.static('public'));
... ...
app.use('/agendash', agendash);
app.listen(8080, '0.0.0.0')

在express中通过app.use方法,可以加载路由,agendash作为一个插件,直接通过调用use方法,就可以添加到express的路由中,项目中,将agendash的相关路由,添加到/agendash下,这样的话,访问地址就是http://localhost:8080/agendash/#

添加agenda任务和任务处理代码

首先实例化一个agenda对象,设置mongodb数据库连接地址,agenda会处理mongodb连接。

通过agenda实例的define方法,就可以绑定事件和处理函数,第一个参数是事件名称,第二个就是处理事件的回调函数;因为处理是过程是异步的,所以结束之后要调用done

agenda在初始化完成之后,会回调绑定的ready方法,在ready中,我们就可以调用agenda的every函数,创建新的定时任务。

将agenda实例作为参数传给Agendash,后者就会生成能够操作agenda实例的router。

var Agenda = require('agenda');
var Agendash = require('agendash');

var mongoConnectionString = config['agendaMongodbUrl']
var agenda = new Agenda({db: {address:mongoConnectionString}})

agenda.define('delete old users', function(job, done) {
  console.log("we will delete user here")
  done()
});

agenda.on('ready', function() {
  //agenda.every('3 minutes', 'delete old users');
  agenda.every('*/5 * * * *', 'delete old users');
  agenda.start();
});

export default Agendash(agenda)

编写简单的测试

完成编码之后,我们通过supertest编写简单测试,检查是否可以成功连接mongodb并启动agendash;要成功运行测试,必须在本地安装mongodb。

如代码所示,我们会测试连接agendash的api接口,检查返回的json数据,是否符合我们创建的定时任务。如果一致,那么测试通过

const app = require('../lib')
const request = require('supertest');
var assert = require('assert');

describe('GET /agendash/api', function () {
  it('should return the correct overview', function (done) {
    request(app).get('/agendash/api')
    .expect(200)
    .expect(function (res) {
      assert(res.body.title, 'Agendash')
    })
    .end(done)
  })
})

添加集成相关配置

因为项目使用了es6语法,所以需要集成babel才能运行程序和测试。我们在package.json中,添加start和test命令的script,在运行和测试的时候,都会用babel来实时编译es6代码。为了成功的运行mocha测试,我们还需要设置.babelrc配置文件。

# package.json
... ...
"scripts": {
    "start": "babel-node lib/index.js --presets es2015,stage-2",
    "test": "./node_modules/.bin/mocha --compilers js:babel-core/register ./test"
}

# .babelrc
{
  "presets": ["es2015","stage-2"],
  "plugins": []
}

... ...

# 然后我们就可以运行程序
npm install
npm start

如果不想自己安装mongodb或者在本机安装node_modules,项目的源代码中提供了docker-compose配置文件,只需要运行docker-compose up命令,就可以运行服务。然后打开浏览器查看http://localhost:8080/agendash

示例代码 https://github.com/liuwill-projects/agenda-cron-demo

文/liuwill(简书作者)
原文链接:用agenda和agendash管理定时任务
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。


liuwill
101 声望4 粉丝