在egg定時任務里增加多一種類型, 用在多pod集群下, 定時任務只跑一遍。

雖然我覺得這個功能完全可以放在項目業務自己實現, 不用集成進內部框架, 但還是去看了一下, 正好想了解一下egg的定時任務是怎么做的。
egg-schedule
這是egg用到的定時任務插件, 源碼看起來不難, 花了點時間在時間調度的邏輯。關鍵在三個函數。
start() { /* istanbul ignore next */ if (this.agent.schedule.closed) return; if (this.schedule.immediate) { this.logger.info(`[Timer] ${this.key} next time will execute immediate`); setImmediate(() => this.handler()); } else { this._scheduleNext(); } }
首先, 如果是設置了immediate, 則調用node自身的setImmediate方法。
如果不是,則過入_scheduleNext函數。
_scheduleNext() { /* istanbul ignore next */ if (this.agent.schedule.closed) return; // get next tick const nextTick = this.getNextTick(); if (nextTick) { this.logger.info(`[Timer] ${this.key} next time will execute after ${nextTick}ms at ${utility.logDate(new Date(Date.now() + nextTick))}`); this.safeTimeout(() => this.handler(), nextTick); } else { this.logger.info(`[Timer] ${this.key} reach endDate, will stop`); } }
從這里可以大概看出, 獲取任務的下一個執行時間, 然后調用setTimeout方法, 還是node自身的。這里有一點, 之所以要用safe-timeout庫, 是因為node自身的setTimeout函數設置的時間間隔是一個32位的整數, 換算成時間, 只能存從之后24天的時間, 如果大於24天, 可能就會溢出, 達不到想要的定時效果。
接下來, 就是看怎樣獲取定時任務的下次執行時間。答案在getNextTick函數。
getNextTick() { // interval-style if (this.schedule.interval) return ms(this.schedule.interval); // cron-style if (this[CRON_INSTANCE]) { // calculate next cron tick const now = Date.now(); let nextTick; let nextInterval; // loop to find next feature time do { try { nextInterval = this[CRON_INSTANCE].next(); nextTick = nextInterval.getTime(); } catch (err) { // Error: Out of the timespan range return; } } while (now >= nextTick); return nextTick - now; } }
如果是cron, 拿到下次定時的時間, 再與當前時間比較:
如果下次定時時間大於當前時間, 說明任務還沒到執行時間, 返回下次定時時間與當前時間的差值, 作為setTimeout的參數;
如果下次定時時間小於當前時間, 則讓下次定單時間一直追上當前時間(while循環),直到追上當前時間。
