在軟件系統的運維中,總有一些事件,須要在特定的時間來觸發執行,這個時候,咱們須要用到定時任務。javascript
agenda是nodejs實現的基於mongodb數據庫的分佈式定時任務管理系統。agendash則爲agenda提供了一個web管理控制檯。java
這篇文章,經過一個demo項目,演示瞭如何使用agenda來管理定時任務。node
最先用crontab管理來管理定時任務的時候,須要手動去服務器上設置任務,而後還要在服務器上部署定時執行的程序。整個過程依賴手工操做,用起來感受特別不踏實。那時候在想,應該有一種更方便的管理定時任務的機制,因此儘可能在設計上減小定時任務的使用,只在最必要的時候纔會使用。git
後來看了不少技術分享,看到知乎的分享他們的運維繫統的經驗,實現了一個能夠配置,而且有精美的管理後臺的定時任務管理系統,可以在運維後臺方便的對任務進行操做。es6
如今已經能夠簡單的使用agendash和agenda集成,就方便了實現了任務管理,從而能夠更專一的實現業務邏輯github
雖然不須要本身再去發明輪子是很是方便的事情,可是仍是應該試着去本身動手作一些嘗試,實際實現起來成本比較高,可是思考一下如何實現,仍是有價值的。web
agenda經過mongodb實現了任務在定時任務處理集羣中的共享和鎖定,從而實現了分佈式執行定時任務的功能,實現了更高的可用性;mongodb
再結合做爲express插件開發的agendash,咱們就能夠經過管理後臺查詢和管理定時任務。docker
若是我本身實現的話,首先要考慮定時任務如何設置,就是語法,agenda並無本身發明輪子,而是使用了已有的cron模塊,該模塊的定時任務配置語法,和crontab同樣,是形如*/5 * * * *
的形式。shell
咱們要考慮的是定時任務的機制,程序執行以後,一直在進程中輪詢,是否符合觸發條件,若是符合觸發條件,就會觸發設置的要執行的業務邏輯相關代碼,crontab實際上也是觸發執行shell腳本的代碼。最後由業務邏輯代碼根據本身的實際狀況運行。
任務的調用,能夠用觀察者模式
實現。在nodejs中,能夠方便的使用回調函數,將任務的名字和回調函數綁定,這樣,任務條件達到的時候,就會觸發執行回調函數;能夠結合nodejs的queue隊列模塊來實現。
只有把任務用專有的數據結構存儲起來,纔有可能實現某種任務自己的邏輯,須要什麼樣的邏輯,就須要什麼樣的數據結構作支撐。所以mongodb中要存儲的數據包括:執行條件、任務名稱、建立時間、上次開始時間、上次完成時間、下次執行時間、目前是否在鎖定狀態。
這樣,某個節點執行是,正好符合條件,它先設置任務狀態爲鎖定,而後調用回調函數,完成以後,在解除鎖定,而且設置相應的時間;若是出現程序意外崩潰,其餘節點檢查是否超過必定處理時間,會將任務的鎖定狀態解除,而後接下來的其餘節點,就會檢查而且執行任務。
agenda自己運行,是不須要web服務器;要使用agendash,目前必須安裝express,將agendash做爲插件添加到express中,就能夠正常的訪問。
演示程序的具體步驟以下:
安裝依賴包
搭建express腳手架
添加agenda任務和任務處理代碼
編寫簡單的測試
添加集成相關配置
項目須要用到的依賴包分爲三類,基本業務邏輯須要,es6編譯相關、還有測試依賴包。如下命令不包含babel相關依賴,請參看其餘網上的教程。
npm install --saveagenda agendash express mongoose ejs npm install --save-dev mocha chai supertest
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對象,設置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
文/liuwill(簡書做者)
原文連接:用agenda和agendash管理定時任務著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。