Node.js 战略级定时任务:自动清理过期 Token
你是否遇到过 Token 过期导致用户无法登录的问题?在高并发场景下,手动清理显然不是个好主意,自动定时清理才是王道。
业务场景
假设你有一个在线商城,用户登录后会生成一个 JWT Token,有效期为 7 天。超过有效期后,Token 应该被自动清理,防止数据库膨胀和提高安全性。
技术选型
- cron:用于定时任务调度
- bull:用于任务队列管理
- MongoDB:存储 Token
实现步骤
1. 安装依赖
npm install cron bull mongoose
2. 连接 MongoDB
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/your-database', { useNewUrlParser: true, useUnifiedTopology: true });
const db = mongoose.connection;
db.on('error', console.error.bind(console, '连接错误:'));
db.once('open', () => {
console.log('数据库连接成功');
});
3. 定义 Token 模型
const tokenSchema = new mongoose.Schema({
token: String,
expiresAt: { type: Date, index: { expires: 1 } }
});
const Token = mongoose.model('Token', tokenSchema);
4. 创建定时任务
使用 cron 创建一个定时任务,每 1 小时执行一次。
const cron = require('cron');
const ClearTokenJob = new cron.CronJob('0 * * * *', async () => {
await Token.deleteMany({ expiresAt: { $lt: new Date() } });
console.log('过期 Token 已清理');
});
ClearTokenJob.start();
5. 使用 Bull 管理任务队列
Bull 可以确保任务的可靠执行,即使 Node.js 进程崩溃也能恢复。
const Queue = require('bull');
const clearTokenQueue = new Queue('clearTokenQueue', 'redis://127.0.0.1:6379');
clearTokenQueue.process(async (job, done) => {
await Token.deleteMany({ expiresAt: { $lt: new Date() } });
console.log('过期 Token 已清理');
done();
});
6. 定时将任务加入队列
结合 cron 和 bull,确保任务定时加入队列。
const cron = require('cron');
const ClearTokenJob = new cron.CronJob('0 * * * *', () => {
clearTokenQueue.add();
});
ClearTokenJob.start();
7. 监控任务执行
使用 Bull 的内置监控功能,确保任务执行情况。
clearTokenQueue.on('completed', (job, result) => {
console.log(`任务完成: ${job.id}, 结果: ${result}`);
});
clearTokenQueue.on('failed', (job, err) => {
console.error(`任务失败: ${job.id}, 错误: ${err}`);
});
8. 高可用配置
在生产环境中,确保定时任务的高可用性。
- Redis 集群:配置 Redis 集群,确保任务队列的高可用性。
- 多实例:在多台服务器上运行定时任务,确保任务不会因为单点故障而失败。
9. 部署和测试
部署到生产环境前,务必进行充分的测试。
- 本地测试:使用
nodemon进行本地测试,确保代码无误。 - Docker:使用 Docker 部署,确保环境一致性。
# Dockerfile
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "app.js"]
10. 日志记录
记录日志,便于问题排查和监控。
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
clearTokenQueue.process(async (job, done) => {
try {
await Token.deleteMany({ expiresAt: { $lt: new Date() } });
logger.info('过期 Token 已清理');
done();
} catch (err) {
logger.error(`清理 Token 失败: ${err}`);
done(err);
}
});
11. 安全性考虑
- Token 签名:确保 JWT Token 的签名安全。
- 环境变量:将敏感信息(如数据库连接字符串)存储在环境变量中。
const secret = process.env.SECRET_KEY;
const jwt = require('jsonwebtoken');
const generateToken = (userId) => {
const token = jwt.sign({ userId }, secret, { expiresIn: '7d' });
return token;
};
const verifyToken = (token) => {
try {
const decoded = jwt.verify(token, secret);
return decoded;
} catch (err) {
return null;
}
};
12. 优化建议
- 索引优化:在
expiresAt字段上添加索引,提高查询效率。 - 任务调度:根据业务需求调整
cron的调度频率。
const tokenSchema = new mongoose.Schema({
token: String,
expiresAt: { type: Date, index: { expires: 1 } } // 添加索引
});
13. 常见问题
- Token 未被清理:检查
expiresAt字段是否正确设置。 - 任务执行失败:查看日志文件,确保 Redis 连接无误。
14. 其他工具推荐
如果你对 cron 和 bull 的组合感到复杂,可以考虑使用 Hey Cron。Hey Cron 是一个高性能、易用的定时任务管理工具,支持多种任务调度方式和集成方式,能够显著简化任务管理的复杂度。
# 安装 Hey Cron
npm install hey-cron
const { Cron } = require('hey-cron');
const clearTokenCron = new Cron('0 * * * *', async () => {
await Token.deleteMany({ expiresAt: { $lt: new Date() } });
logger.info('过期 Token 已清理');
});
clearTokenCron.start();
15. 总结
通过上述步骤,你可以在 Node.js 中实现高效、稳定的定时清理过期 Token 功能。结合 Hey Cron,可以进一步简化任务管理,提高系统的可维护性和可靠性。