NestJS提醒功能实现咨询:如何定时触发移动端通知
问题
我在NestJS中开发了一个POST接口,用于添加一个Date对象,该对象对应向移动端发送通知的日期和时间。我需要检测所有用户的提醒是否已到期,从而触发移动端提醒。以下是我在NestJS中编写的函数:
import { Injectable } from '@nestjs/common' import { UserRepository } from '../auth/repositories/user.repository' import { User } from '@vitabotx/common/entities/auth/user.entity' @Injectable() export class NotificationsCronService { constructor(private readonly userRepository: UserRepository) {} async sleepReminderCron() { const users: User[] = await this.userRepository.getAllUsersForSleepReminder() // Set up interval to check reminders continuously const interval = setInterval(async () => { const currentDate = new Date() for (const user of users) { for (const reminder of user.userReminder) { if (currentDate >= reminder.time) { console.log( `User ${user.id} should receive sleep reminder.` ) } } } }, 1000) setTimeout( () => { clearInterval(interval) }, 59 * 60 * 1000 ) } }
我原本考虑使用setInterval和setTimeout每秒检查提醒是否到期,而非每分钟查询数据库。想请教业界其他项目实现此类场景的推荐方案是什么?
推荐方案
1. 基于NestJS内置定时任务实现
直接用NestJS的@nestjs/schedule模块做定时调度,比如每分钟执行一次查询,只拉取已到期且未处理的提醒,而非全量用户数据:
import { Injectable, Logger } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { UserRepository } from '../auth/repositories/user.repository'; @Injectable() export class NotificationsCronService { private readonly logger = new Logger(NotificationsCronService.name); constructor(private readonly userRepository: UserRepository) {} // 每分钟执行一次 @Cron('* * * * *') async handleSleepReminder() { const now = new Date(); // 查询当前时间已到期、未发送的提醒 const dueReminders = await this.userRepository.findUnsentDueReminders(now); for (const reminder of dueReminders) { this.logger.log(`触发用户${reminder.userId}的睡眠提醒`); // 调用移动端推送接口 // 标记提醒为已发送,避免重复触发 await this.userRepository.markReminderAsSent(reminder.id); } } }
这种方式不用手动管理setInterval,NestJS会自动维护任务生命周期,且每次只处理需要触发的记录,性能更可控。
2. 消息队列+延迟任务方案
如果要求提醒触发精确到秒,推荐用支持延迟任务的消息队列(比如BullMQ、RabbitMQ延迟插件):
- 用户创建提醒时,直接把推送任务发送到队列,设置延迟时间为「提醒时间 - 当前时间」
- 到达指定时间后,队列自动触发任务执行推送
- 无需轮询数据库,资源占用更低,时效性更精准
示例(BullMQ):
// 创建提醒时添加延迟任务 async createReminder(reminderData) { const delay = reminderData.time.getTime() - Date.now(); if (delay > 0) { await this.reminderQueue.add('send-sleep-reminder', { userId: reminderData.userId, }, { delay }); } } // 消费任务 this.reminderQueue.process('send-sleep-reminder', async (job) => { const { userId } = job.data; console.log(`触发用户${userId}的睡眠提醒`); // 执行移动端推送逻辑 });
3. 优化轮询查询逻辑
如果坚持用轮询方案,别一次性拉全量用户:
- 在
userReminder表的time和isSent字段上建联合索引 - 每次轮询直接查数据库中当前时间已到期且未处理的提醒,减少内存占用和循环开销
4. 分布式场景下的任务控制
如果服务是分布式部署,要避免多实例重复执行任务:
- 用Redis分布式锁,确保同一时间只有一个实例执行定时任务
- 或者用专门的分布式任务调度系统(比如XXL-JOB)统一管理任务
内容的提问来源于stack exchange,提问作者Ghosty
相关产品推荐
相关产品推荐

