schangxiang@126.com
2025-09-19 0821aa23eabe557c0d9ef5dbe6989c68be35d1fe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# egg-schedule
 
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][codecov-image]][codecov-url]
[![David deps][david-image]][david-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]
 
[npm-image]: https://img.shields.io/npm/v/egg-schedule.svg?style=flat-square
[npm-url]: https://npmjs.org/package/egg-schedule
[travis-image]: https://img.shields.io/travis/eggjs/egg-schedule.svg?style=flat-square
[travis-url]: https://travis-ci.org/eggjs/egg-schedule
[codecov-image]: https://codecov.io/github/eggjs/egg-schedule/coverage.svg?branch=master
[codecov-url]: https://codecov.io/github/eggjs/egg-schedule?branch=master
[david-image]: https://img.shields.io/david/eggjs/egg-schedule.svg?style=flat-square
[david-url]: https://david-dm.org/eggjs/egg-schedule
[snyk-image]: https://snyk.io/test/npm/egg-schedule/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/egg-schedule
[download-image]: https://img.shields.io/npm/dm/egg-schedule.svg?style=flat-square
[download-url]: https://npmjs.org/package/egg-schedule
 
A schedule plugin for egg, has been built-in plugin for egg enabled by default.
 
It's fully extendable for developer and provide a simple built-in TimerStrategy.
 
## Usage
 
Just add you job file to `{app_root}/app/schedule`.
 
```js
// {app_root}/app/schedule/cleandb.js
const Subscription = require('egg').Subscription;
 
class CleanDB extends Subscription {
  /**
   * @property {Object} schedule
   *  - {String} type - schedule type, `worker` or `all` or your custom types.
   *  - {String} [cron] - cron expression, see [below](#cron-style-scheduling)
   *  - {Object} [cronOptions] - cron options, see [cron-parser#options](https://github.com/harrisiirak/cron-parser#options)
   *  - {String | Number} [interval] - interval expression in millisecond or express explicitly like '1h'. see [below](#interval-style-scheduling)
   *  - {Boolean} [immediate] - To run a scheduler at startup
   *  - {Boolean} [disable] - whether to disable a scheduler, usually use in dynamic schedule
   *  - {Array} [env] - only enable scheduler when match env list
   */
  static get schedule() {
    return {
      type: 'worker',
      cron: '0 0 3 * * *',
      // interval: '1h',
      // immediate: true,
    };
  }
 
  async subscribe() {
    await this.ctx.service.db.cleandb();
  }
}
 
module.exports = CleanDB;
```
 
You can also use function simply.
 
```js
exports.schedule = {
  type: 'worker',
  cron: '0 0 3 * * *',
  // interval: '1h',
  // immediate: true,
};
 
exports.task = async function (ctx) {
  await ctx.service.db.cleandb();
};
```
 
## Overview
 
`egg-schedule` supports both cron-based scheduling and interval-based scheduling.
 
Schedule decision is being made by `agent` process. `agent` triggers a task and sends message to `worker` process. Then, one or all `worker` process(es) execute the task based on schedule type.
 
To setup a schedule task, simply create a job file in `{app_root}/app/schedule`. A file contains one job and export `schedule` and `task` properties.
 
The rule of thumbs is one job per file.
 
## Task
 
Task is a class which will be instantiated every schedule, and `subscribe` method will be invoked.
 
You can get anonymous context with `this.ctx`.
 
- ctx.method: `SCHEDULE`
- ctx.path: `/__schedule?path=${schedulePath}&${schedule}`.
 
To create a task, `subscribe` can be generator function or async function. For example:
 
```js
// A simple logger example
const Subscription = require('egg').Subscription;
class LoggerExample extends Subscription {
  async subscribe() {
    this.ctx.logger.info('Info about your task');
  }
}
```
 
```js
// A real world example: wipe out your database.
// Use it with caution. :)
const Subscription = require('egg').Subscription;
class CleanDB extends Subscription {
  async subscribe() {
    await this.ctx.service.db.cleandb();
  }
}
```
 
## Scheduling
 
`schedule` is an object that contains one required property, `type`, and optional properties, `{ cron, cronOptions, interval, immediate, disable, env }`.
 
### Cron-style Scheduling
 
Use [cron-parser](https://github.com/harrisiirak/cron-parser).
 
> Note: `cron-parser` support `second` as optional that is not supported by linux crontab.
>
> `@hourly / @daily / @weekly / @monthly / @yearly` is also supported.
 
```bash
*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    |
│    │    │    │    │    └ 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)
```
 
Example:
 
```js
// To execute task every 3 hours
exports.schedule = {
  type: 'worker',
  cron: '0 0 */3 * * *',
  cronOptions: {
    // tz: 'Europe/Athens',
  }
};
```
 
### Interval-style Scheduling
 
To use `setInterval`, and support [ms](https://www.npmjs.com/package/ms) conversion style
 
Example:
 
```js
// To execute task every 3 hours
exports.schedule = {
  type: 'worker',
  interval: '3h',
};
```
 
**Notice: Egg built-in TimerStrategy will schedule each execution at a fix rate, regardless of its execution time. So you have to make sure that your actual execution time of your `task/subscribe` must be smaller than your delay time.**
 
### Schedule Type
 
**Build-in support is:**
 
- `worker`: will be executed in one random worker when schedule run.
- `all`: will be executed in all workers when schedule run.
 
**Custom schedule:**
 
To create a custom schedule, simply extend `agent.ScheduleStrategy` and register it by `agent.schedule.use(type, clz)`.
You can schedule the task to be executed by one random worker or all workers with the built-in method `this.sendOne(...args)` or `this.sendAll(...args)` which support params, it will pass to `subscribe(...args)` or `task(ctx, ...args)`.
 
```js
// {app_root}/agent.js
module.exports = function(agent) {
  class CustomStrategy extends agent.ScheduleStrategy {
    start() {
      // such as mq / redis subscribe
      agent.notify.subscribe('remote_task', data => {
        this.sendOne(data);
      });
    }
  }
  agent.schedule.use('custsom', CustomStrategy);
};
```
 
Then you could use it to defined your job:
 
```js
// {app_root}/app/schedule/other.js
const Subscription = require('egg').Subscription;
class ClusterTask extends Subscription {
  static get schedule() {
    return {
      type: 'custom',
    };
  }
  async subscribe(data) {
    console.log('got custom data:', data);
    await this.ctx.service.someTask.run();
  }
}
```
 
## Dynamic schedule
 
```js
// {app_root}/app/schedule/sync.js
module.exports = app => {
  class SyncTask extends app.Subscription {
    static get schedule() {
      return {
        interval: 10000,
        type: 'worker',
        // only start task when hostname match
        disable: require('os').hostname() !== app.config.sync.hostname,
        // only start task at prod mode
        env: [ 'prod' ],
      };
    }
    async subscribe() {
      await this.ctx.sync();
    }
  }
  return SyncTask;
}
```
 
## Configuration
 
### Logging
 
See `${appInfo.root}/logs/{app_name}/egg-schedule.log` which provided by [config.customLogger.scheduleLogger](https://github.com/eggjs/egg-schedule/blob/master/config/config.default.js).
 
```js
// config/config.default.js
config.customLogger = {
  scheduleLogger: {
    // consoleLevel: 'NONE',
    // file: path.join(appInfo.root, 'logs', appInfo.name, 'egg-schedule.log'),
  },
};
```
 
### Customize directory
 
If you want to add additional schedule directories, you can use this config.
 
```js
// config/config.default.js
config.schedule = {
  directory: [
    path.join(__dirname, '../app/otherSchedule'),
  ],
};
```
 
## Testing
 
`app.runSchedule(scheduleName)` is provided by `egg-schedule` plugin only for test purpose.
 
Example:
 
```js
it('test a schedule task', async function () {
  // get app instance
  await app.runSchedule('clean_cache');
});
```
 
## Questions & Suggestions
 
Please open an issue [here](https://github.com/eggjs/egg/issues).
 
## License
 
[MIT](https://github.com/eggjs/egg-schedule/blob/master/LICENSE)