egg定时任务原理
Posted cool-fire
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了egg定时任务原理相关的知识,希望对你有一定的参考价值。
在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循环),直到追上当前时间。
以上是关于egg定时任务原理的主要内容,如果未能解决你的问题,请参考以下文章
egg.js redis subscribe multiple channel