nodejs定时任务一直是个让人头疼的问题,我在做之前就已经看到过很多人发问题问到pm2部署项目以后,多线程的问题下,
如何关闭定时任务,
或者任务被多个线程同时执行
这类问题。
第一章手拉手开发nodejs电影cms系统①:内容规划,导航分类,视频数据,用户,留言
第二章手拉手开发nodejs电影cms系统②:定时任务,视频源数据,初始化数据
第三章手拉手开发nodejs电影cms系统③:宝塔面板懒人部署
<<< 用nodejs+mongodb开发了套影视站点cms,觉得可以的大佬可以送个star >>>
pm2部署无法关闭任务(后台)
pm2部署无法关闭任务是因为,用户(管理员)开启定时任务的时候,可能被pm2分发到了B线程(假设电脑6核心12线程),你:我电脑就双核哪来的六核心?
不好意思,我的12线程。
扯远了,扯回来。
当用户(管理员)想关闭的时候pm2可能把任务分发到了G线程上了,但是G线程上并没有执行这个任务,就会报错。
定时任务被多个线程同时执行?
那是因为这个定时任务是项目部署开启服务的时候就设置好了,到了时间,pm2复制多份任务执行(一毛一样),当然就会重复跑任务。
怎么避免合适?
试想一下,如果我们项目用pm2部署,复制了12份node项目。但是我们可以使用别的进程守护工具,例如forever(单份,单线程)守护定时任务。
说人话:就是分两个项目部署,定时任务使用一个单线程守护工具,守护。这样,定时任务到时间以后就是单线程跑了,不会多线程同时调用。随之而来的一个问题,怎么通讯??pm2那边的服务怎么通知forever这边守护的的进程开启关闭任务。(http服务啊)
假设pm2守护的项目监听的是8888端口,我们forever这边的js开启的http服务可以监听9999。两者之间通讯就发post请求。(因为本机的原因。所以很快,秒收到)这样,通讯的问题解决,就可以随时动态的创建/删除 定时任务了。也不会出现多线程同时执行的问题。
视频源录入(后台)
上一章说了,视频数据录入格式。播放源如何录入,长啥样?
和留言关联视频表,关联用户表一样。视频源的数据也需要单独出来,因为如果不单独出来,我们只是查询一下电影列表,不需要展示播放源列表的时候,没有必要无端带出来那么多的数据
同样使用mongodb管道联表操作就可以很容易关联出源的数据。
初始化数据(后台)
可能刚入坑nodejs的cxk没懂,简称就是项目初始化默认就必须有的数据,比如博客总得有个默认的后台系统的root用户吧,默认的顶级分类,一些默认的配置,从哪来??肯定不会天上掉下来。
nodejs pakcage。json的script字段用于定义一些命令。前端的肯定知道,npm run dev,npm run build这些你肯定常用
你完全可以再script定义你的初始化数据命令。比如我
"scripts": {
"build": "node ./build/initDataBase.js",
"restore": "mongorestore -d movie ./backup/movie",
"backup": "mongodump -d movie -o ./backup/movie"
}
build用于连接数据库,删除原有的数据库,创建全新的数据库,创建字段索引,并向数据库中插入默认的数据,比如电影cms系统的默认管理员信息。初始化的数据
const mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
config = require('../utils/config.js'),
dbConfig = config.project;
let connectURL = 'mongodb://127.0.0.1:27017';
let client = MongoClient.connect(connectURL, { useNewUrlParser: true, useUnifiedTopology: true });
client.then(async (db) => {
let DB = db.db(dbConfig.dbName);
await new Promise(async (resolve, reject) => {
console.log('正在清除原始数据表');
await DB.dropCollection('session1').catch(err => {});
await DB.dropCollection('session2').catch(err => {});
await DB.dropCollection('config').catch(err => {});
await DB.dropCollection('logs').catch(err => {});
await DB.dropCollection('message').catch(err => {});
await DB.dropCollection('other').catch(err => {});
await DB.dropCollection('user').catch(err => {});
await DB.dropCollection('video_info').catch(err => {});
await DB.dropCollection('video_list').catch(err => {});
resolve()
})
.then(() => {
console.log('原始数据表清除完成');
})
.catch(err => {
console.log('+++', err);
})
await new Promise(async (resolve, reject) => {
console.log('开始重建数据表');
await DB.createCollection('session1');
await DB.createCollection('session2');
await DB.createCollection('config');
await DB.createCollection('logs');
await DB.createCollection('message');
await DB.createCollection('other');
await DB.createCollection('user');
await DB.createCollection('video_info');
await DB.createCollection('video_list');
resolve()
})
.then(() => {
console.log('数据表重建完成');
})
await new Promise(async (resolve, reject) => {
console.log('开始创建用户数据');
// 用户字段索引
let userColl = DB.collection('user');
// 索引字段
await userColl.createIndexes([
{key:{userName: 1}},
{key:{passWord: 1}},
{key:{display: 1}},
{key:{admin: 1}},
{key:{grade_id: 1}},
{key:{default: 1}}
]);
await userColl.insertOne({
userName: 'root',
passWord: 'e10adc3949ba59abbe56e057f20f883e', // 123456 md5
nickName: '网站所有者',
admin: true,
display: true,
default: true,
grade_id: 2, // 0用户 1管理员 2root用户
});
resolve();
})
.then(res=>{
console.log('用户数据创建成功');
})
});