1、简单应用
文档地址:
英文:
1、安装依赖
npm install --save @nestjs/schedule
2、在app.module.ts文件中导入ScheduleModule
ScheduleModule.forRoot()
此导入会初始化调用任务,注册所有代码中已经存在的延期任务(Timeout)、间隔任务(Interval)和其他形式的定时任务。
3、添加一个task.service
nest g service task
并加入以下代码,并且需要在app.module.ts中,providers中加入TaskService
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression, Interval, Timeout } from '@nestjs/schedule';
import * as dayjs from 'dayjs';
@Injectable()
export class TaskService {
@Cron('45 * * * * *', { name: "ExecCron" })
execCron() {
console.log(`ExecCron作业每分钟执行一次${dayjs(new Date()).format('YYYY-MM-DD hh:mm:ss')}`)
}
@Cron(CronExpression.EVERY_30_SECONDS)
execSysCron() {
console.log(`每30秒执行一次${dayjs(new Date()).format('YYYY-MM-DD hh:mm:ss')}`)
}
@Timeout(20000)
timeoutTask() {
console.log(`20s以后执行,只执行一次${dayjs(new Date()).format('YYYY-MM-DD hh:mm:ss')}`)
}
@Interval(20000)
intervalTask() {
console.log(`间隔20s执行一次${dayjs(new Date()).format('YYYY-MM-DD hh:mm:ss')}`)
}
}
此时,使用npm start运行目录,会定时输出三种类型任务的执行结果:
2、动态应用
很多时候,我们需要灵活的添加定时任务,而不是在项目中集成定时任务。@nestjs/schedule包也提供了动态API,可以帮助我们更加灵活的处理定时任务,主要通过任务的name属性交互处理任务。
在app.controller.ts中导入@nestjs/schedule包下的SchedulerRegistry和cron包下的CronJob,
因为用到了日期格式化,我们把dayjs也一起倒入。
import { SchedulerRegistry } from '@nestjs/schedule';
import { CronJob } from 'cron';
import * as dayjs from 'dayjs';
这里引用cron时,要根据 @nestjs/schedule包中引用的cron版本安装,cron版本不一致可能会引起代码报错。无法正常执行,通过查看文件node_modules->@nestjs->schedule->package.json,我们可以看到cron的版本,我们项目中也要安装此版本号的cron包。
在app.contrller.ts中添加以下代码
@Public()
@ApiOperation({ summary: '停止延时任务' })
@Get('stopTimeout/:name')
stopTimeout(@Param('name') name: string) {
const timeout = this.schedulerRegistry.getTimeout(name);
clearTimeout(timeout);
}
@Public()
@ApiOperation({ summary: '添加延时任务' })
@Get('addTimeout/:name/:milliseconds')
addTimeout(@Param('name') name: string, @Param('milliseconds', ParseIntPipe) milliseconds: number) {
const callback = () => {
//到时间后执行任务
console.log(`Timeout ${name} executing after (${milliseconds})!`);
};
const timeout = setTimeout(callback, milliseconds);
this.schedulerRegistry.addTimeout(name, timeout);
}
@Public()
@ApiOperation({ summary: '删除延时任务', description: '通过任务name删除延时任务' })
@Delete('delTimeout/:name')
deleteTimeout(@Param('name') name: string) {
this.schedulerRegistry.deleteTimeout(name);
// console.log(`Timeout ${name} deleted!`);
return `Timeout ${name} deleted!`
}
@Public()
@ApiOperation({ summary: '获取所有延时任务' })
@Get('getTimeout')
getTimeouts() {
const timeouts = this.schedulerRegistry.getTimeouts();
// timeouts.forEach(key => console.log(`Timeout: ${key}`));
return timeouts
}
@Public()
@Get('stopInterval/:name')
stopInterval(@Param('name') name: string) {
const interval = this.schedulerRegistry.getInterval(name);
clearInterval(interval);
}
@Public()
@ApiOperation({ summary: '添加一个间隔任务' })
@Get('addInterval/:name/:milliseconds')
addInterval(@Param('name') name: string, @Param('milliseconds') milliseconds: number) {
const callback = () => {
//间隔执行任务
console.log(`Interval ${name} executing at time (${milliseconds})!`);
};
const interval = setInterval(callback, milliseconds);
this.schedulerRegistry.addInterval(name, interval);
}
@Public()
@ApiOperation({ summary: '删除间隔任务' })
@Delete('delInterval/:name')
deleteInterval(@Param('name') name: string) {
this.schedulerRegistry.deleteInterval(name);
console.log(`Interval ${name} deleted!`);
}
@Public()
@ApiOperation({ summary: '获取所有间隔任务' })
@Get('getIntervals')
getIntervals() {
const intervals = this.schedulerRegistry.getIntervals();
intervals.forEach(key => console.log(`Interval: ${key}`));
}
@Public()
@ApiOperation({ summary: '停止cron任务' })
@Get('stopJob/:name')
stopJob(@Param("name") name: string) {
const job = this.schedulerRegistry.getCronJob(name)
if (job != null) {
job.stop()
console.log(job.lastDate())
console.log(`${name}已停止`)
return `${name}已停止`
}
return `${name}未找到`
}
@Public()
@ApiOperation({ summary: '添加cron任务' })
@Get('addJob/:name/:seconds')
addCronJob(@Param('name') name: string, @Param('seconds') seconds: string) {
const job = new CronJob(`${seconds} * * * * *`, () => {
//执行内容,这里我们可以封装一些通用的任务,比如:调用一个处理数据的接口
console.log(`job ${name} to run at ${dayjs(new Date()).format("YYYY-MM-DD hh:mm:ss")}!`);
});
this.schedulerRegistry.addCronJob(name, job);
job.start();
return `job ${name} added for each minute at ${seconds} seconds!`
}
@Public()
@ApiOperation({ summary: '删除cron任务' })
@Delete('delJob/:name')
deleteCron(@Param('name') name: string) {
this.schedulerRegistry.deleteCronJob(name);
return `job ${name} deleted!`;
}
@Public()
@ApiOperation({ summary: '获取所有cron任务' })
@Get('getCrons')
getCrons() {
const jobs = this.schedulerRegistry.getCronJobs();
let cronTasks = []
jobs.forEach((value, key, map) => {
cronTasks.push(key)
});
return cronTasks
}
代码中,针对cron任务,间隔任务,延时任务分别做了增、删、停止(改)、查询。
这样我们可以结合我们自己的场景搭建一个单独的任务调度服务,用来处理我们可能遇到的所有定时任务的需求。
测试cron任务接口:
新增cron任务
获取所欲cron任务:
停止一个cron任务
停止后,我们可以看到,corn-task01不执行了
删除一个任务
删除后,我们再获取所有cron任务,发现corn-task01不见了
以上是学习NestJS任务调度的整个过程,其实也是模拟了真实工作中遇到的任务调度需求。之前一直再用Hangfire做整个工作,有兴趣的也可以学习一下,有两个版本一个基于ASP.NET framework,一个基于.NET Core的。
使用情况:
我是通过接口添加不同任务,目前已经跑了50w+次任务了。