引言
任务调度是现代后端应用中的常见需求,无论是定时数据清理、报表生成还是系统维护,都需要可靠的任务调度机制。NestJS通过@nestjs/schedule
包提供了强大的任务调度功能,但官方文档主要关注静态任务配置。本文将带你探索如何在NestJS中实现动态的Cron任务管理,包括添加、修改和移除任务。
核心概念
1. @nestjs/schedule模块
@nestjs/schedule
是NestJS官方提供的任务调度模块,基于流行的node-cron
库。它提供了:
@Cron()
装饰器:声明静态Cron任务SchedulerRegistry
:用于管理已注册的任务- 间隔任务和超时任务支持
2. 动态任务 vs 静态任务
- 静态任务:在应用启动时通过装饰器定义,生命周期与应用一致
- 动态任务:可在运行时添加、修改和删除,灵活性更高
实现步骤
1. 基础配置
首先安装依赖:
npm install @nestjs/schedule
然后在AppModule中导入:
import { ScheduleModule } from '@nestjs/schedule';
@Module({
imports: [ScheduleModule.forRoot()],
})
export class AppModule {}
2. 创建动态任务服务
核心服务DynamicTaskService
封装了所有动态任务操作:
@Injectable()
export class DynamicTaskService {
constructor(private schedulerRegistry: SchedulerRegistry) {}
// 添加任务
addCronJob(name: string, cronTime: string, callback: () => void) {
const job = new CronJob(cronTime, callback);
this.schedulerRegistry.addCronJob(name, job);
job.start();
}
// 检查任务是否存在
doesCronJobExist(name: string): boolean {
try {
this.schedulerRegistry.getCronJob(name);
return true;
} catch {
return false;
}
}
// 更新任务
updateCronJob(name: string, cronTime: string, callback: () => void) {
this.deleteCronJob(name);
this.addCronJob(name, cronTime, callback);
}
// 删除任务
deleteCronJob(name: string) {
this.schedulerRegistry.deleteCronJob(name);
}
// 获取所有任务
getCronJobs() {
return Array.from(this.schedulerRegistry.getCronJobs().entries())
.map(([name, job]) => ({
name,
cronTime: job.cronTime.source,
running: job.running,
lastExecution: job.lastDate(),
}));
}
}
3. 创建任务管理API
通过REST API暴露任务管理功能:
@Controller('tasks')
export class TasksController {
constructor(private readonly taskService: DynamicTaskService) {}
@Post()
createOrUpdateTask(@Body() body: { name: string; cronTime: string }) {
const taskLogic = () => console.log(`Executing ${body.name}`);
if (this.taskService.doesCronJobExist(body.name)) {
this.taskService.updateCronJob(body.name, body.cronTime, taskLogic);
return { message: 'Task updated' };
} else {
this.taskService.addCronJob(body.name, body.cronTime, taskLogic);
return { message: 'Task created' };
}
}
@Get()
listTasks() {
return this.taskService.getCronJobs();
}
@Delete(':name')
removeTask(@Param('name') name: string) {
this.taskService.deleteCronJob(name);
return { message: 'Task deleted' };
}
}
实际应用示例
1. 创建每日数据备份任务
POST /tasks
{
"name": "daily-backup",
"cronTime": "0 3 * * *" # 每天凌晨3点执行
}
2. 修改为每周备份
POST /tasks
{
"name": "daily-backup",
"cronTime": "0 3 * * 1" # 改为每周一凌晨3点
}
3. 查看所有任务
GET /tasks
# 返回示例
[
{
"name": "daily-backup",
"cronTime": "0 3 * * 1",
"running": true,
"lastExecution": "2023-05-15T03:00:00.000Z"
}
]
高级主题
1. 任务持久化
内存中的任务会在应用重启后丢失,解决方案:
// 启动时从数据库加载任务
async onApplicationBootstrap() {
const savedTasks = await this.taskRepository.find();
savedTasks.forEach(task => {
this.addCronJob(task.name, task.cronTime, this.getTaskLogic(task));
});
}
2. 分布式环境考虑
在多实例部署中,需要:
- 使用分布式锁确保任务只在一个实例执行
- 考虑使用专门的作业队列系统(如BullMQ)
- 或者使用数据库标记防止重复执行
3. 错误处理与监控
增强健壮性:
addCronJob(name: string, cronTime: string, callback: () => void) {
try {
const job = new CronJob(cronTime, () => {
try {
callback();
} catch (error) {
this.logger.error(`Job ${name} execution failed`, error.stack);
}
});
// ...其余代码
} catch (error) {
this.logger.error(`Invalid cron pattern: ${cronTime}`);
throw new BadRequestException('Invalid cron pattern');
}
}
总结
通过@nestjs/schedule
和SchedulerRegistry
,我们可以在NestJS中实现灵活的动态任务管理。关键点包括:
- 使用
CronJob
类创建动态任务 - 通过
SchedulerRegistry
管理任务生命周期 - 提供清晰的API进行任务CRUD操作
- 考虑持久化和分布式场景
这种模式非常适合需要动态调整任务调度策略的应用,如CMS系统、数据分析平台等。根据你的具体需求,可以进一步扩展错误处理、监控和持久化功能。