const res = await ctx.curl('http://www.api.com/cache', { dataType: 'json', }); ctx.app.cache = res.data; }, };
|
這個定時任務(wù)會在每一個 Worker 進(jìn)程上每 1 分鐘執(zhí)行一次,將遠(yuǎn)程數(shù)據(jù)請求回來掛載到 app.cache 上。
任務(wù)
- task 或 subscribe 同時支持 generator function 和 async function。
- task 的入?yún)?nbsp;ctx,匿名的 Context 實(shí)例,可以通過它調(diào)用 service 等。
定時方式
定時任務(wù)可以指定 interval 或者 cron 兩種不同的定時方式。
interval
通過 schedule.interval 參數(shù)來配置定時任務(wù)的執(zhí)行時機(jī),定時任務(wù)將會每間隔指定的時間執(zhí)行一次。interval 可以配置成
- 數(shù)字類型,單位為毫秒數(shù),例如 5000。
- 字符類型,會通過 ms 轉(zhuǎn)換成毫秒數(shù),例如 5s。
module.exports = { schedule: { // 每 10 秒執(zhí)行一次 interval: '10s', }, };
|
cron
通過 schedule.cron 參數(shù)來配置定時任務(wù)的執(zhí)行時機(jī),定時任務(wù)將會按照 cron 表達(dá)式在特定的時間點(diǎn)執(zhí)行。cron 表達(dá)式通過 cron-parser 進(jìn)行解析。
注意:cron-parser 支持可選的秒(linux crontab 不支持)。
* * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ | │ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun) │ │ │ │ └───── month (1 - 12) │ │ │ └────────── day of month (1 - 31) │ │ └─────────────── hour (0 - 23) │ └──────────────────── minute (0 - 59) └───────────────────────── second (0 - 59, optional)
|
module.exports = { schedule: { // 每三小時準(zhǔn)點(diǎn)執(zhí)行一次 cron: '0 0 */3 * * *', }, };
|
類型
框架提供的定時任務(wù)默認(rèn)支持兩種類型,worker 和 all。worker 和 all 都支持上面的兩種定時方式,只是當(dāng)?shù)綀?zhí)行時機(jī)時,會執(zhí)行定時任務(wù)的 worker 不同:
- worker 類型:每臺機(jī)器上只有一個 worker 會執(zhí)行這個定時任務(wù),每次執(zhí)行定時任務(wù)的 worker 的選擇是隨機(jī)的。
- all 類型:每臺機(jī)器上的每個 worker 都會執(zhí)行這個定時任務(wù)。
其他參數(shù)
除了剛才介紹到的幾個參數(shù)之外,定時任務(wù)還支持這些參數(shù):
- cronOptions: 配置 cron 的時區(qū)等,參見 cron-parser 文檔
- immediate:配置了該參數(shù)為 true 時,這個定時任務(wù)會在應(yīng)用啟動并 ready 后立刻執(zhí)行一次這個定時任務(wù)。
- disable:配置該參數(shù)為 true 時,這個定時任務(wù)不會被啟動。
- env:數(shù)組,僅在指定的環(huán)境下才啟動該定時任務(wù)。
執(zhí)行日志
執(zhí)行日志會輸出到 ${appInfo.root}/logs/{app_name}/egg-schedule.log,默認(rèn)不會輸出到控制臺,可以通過 config.customLogger.scheduleLogger 來自定義。
// config/config.default.js config.customLogger = { scheduleLogger: { // consoleLevel: 'NONE', // file: path.join(appInfo.root, 'logs', appInfo.name, 'egg-schedule.log'), }, };
|
動態(tài)配置定時任務(wù)
有時候我們需要配置定時任務(wù)的參數(shù)。定時任務(wù)還有支持另一種寫法:
module.exports = app => { return { schedule: { interval: app.config.cacheTick, type: 'all', }, async task(ctx) { const res = await ctx.curl('http://www.api.com/cache', { contentType: 'json', }); ctx.app.cache = res.data; }, }; };
|
手動執(zhí)行定時任務(wù)
我們可以通過 app.runSchedule(schedulePath) 來運(yùn)行一個定時任務(wù)。app.runSchedule 接受一個定時任務(wù)文件路徑(app/schedule 目錄下的相對路徑或者完整的絕對路徑),執(zhí)行對應(yīng)的定時任務(wù),返回一個 Promise。
有一些場景我們可能需要手動的執(zhí)行定時任務(wù),例如
- 通過手動執(zhí)行定時任務(wù)可以更優(yōu)雅的編寫對定時任務(wù)的單元測試。
const mm = require('egg-mock'); const assert = require('assert');
it('should schedule work fine', async () => { const app = mm.app(); await app.ready(); await app.runSchedule('update_cache'); assert(app.cache); });
|
- 應(yīng)用啟動時,手動執(zhí)行定時任務(wù)進(jìn)行系統(tǒng)初始化,等初始化完畢后再啟動應(yīng)用。參見應(yīng)用啟動自定義章節(jié),我們可以在 app.js 中編寫初始化邏輯。
module.exports = app => { app.beforeStart(async () => { // 保證應(yīng)用啟動監(jiān)聽端口前數(shù)據(jù)已經(jīng)準(zhǔn)備好了 // 后續(xù)數(shù)據(jù)的更新由定時任務(wù)自動觸發(fā) await app.runSchedule('update_cache'); }); };
|
擴(kuò)展定時任務(wù)類型
默認(rèn)框架提供的定時任務(wù)只支持每臺機(jī)器的單個進(jìn)程執(zhí)行和全部進(jìn)程執(zhí)行,有些情況下,我們的服務(wù)并不是單機(jī)部署的,這時候可能有一個集群的某一個進(jìn)程執(zhí)行一個定時任務(wù)的需求。
框架并沒有直接提供此功能,但開發(fā)者可以在上層框架自行擴(kuò)展新的定時任務(wù)類型。
在 agent.js 中繼承 agent.ScheduleStrategy,然后通過 agent.schedule.use() 注冊即可:
module.exports = agent => { class ClusterStrategy extends agent.ScheduleStrategy { start() { // 訂閱其他的分布式調(diào)度服務(wù)發(fā)送的消息,收到消息后讓一個進(jìn)程執(zhí)行定時任務(wù) // 用戶在定時任務(wù)的 schedule 配置中來配置分布式調(diào)度的場景(scene) agent.mq.subscribe(schedule.scene, () => this.sendOne()); } } agent.schedule.use('cluster', ClusterStrategy); };
|
ScheduleStrategy 基類提供了:
- schedule - 定時任務(wù)的屬性,disable 是默認(rèn)統(tǒng)一支持的,其他配置可以自行解析。
- this.sendOne(...args) - 隨機(jī)通知一個 worker 執(zhí)行 task,args 會傳遞給 subscribe(...args) 或 task(ctx, ...args)。
- this.sendAll(...args) - 通知所有的 worker 執(zhí)行 task。
更多建議: