关于node项目的线上部署与守护
预警:本文是作者根据自己的实践自己捣鼓的,十分粗糙,切勿盲从。若要用于线上项目,请多测试是否有坑。欢迎批评沟通与指正;
首先,我们从简单的模型分析
1、上线流程:
覆盖线上代码—》重启哈哈,没错,就是两步
2、多进程的实现:
都知道node是单线程的嘛,为了充分利用CPU性能,我们需要开启多个进程,以使用CPU的多核心
3、进程守护:
既然应用跑起来了,自然不能让它挂了呀,所以需要一个守护进程嘛,
下面,我们来一步一步解决问题
这里先给出代码目录结构:
- rootDir
- node_modules
- dir1
- dir2
- app.sh//程序启动和停止脚本
- cluster.js//用于生成多进程
- error.log
- guard.sh//守护进程脚本
- index.js//node程序入口
- nohup.out
- package.json
1、我们先看看怎么实现多进程就是不按套路出牌 :)
先看看我们的node入文件index.js
//其它业务逻辑
app.use(....)//一些中间件
if(module.parent){//如果是被require的,则露出app
console.log(module);
module.exports = function(){
app.listen('4500');
console.log('listening on port :4500');
}
}else{//否则,直接启动
app.listen('4500');
console.log('listening on port :4500');
}
node中有一个叫Cluster的事例,就是专门来实现多线程的,有兴趣的朋友可以看一看。
官方描述:A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load.
我们就用它实现了我们的多线程集群:cluster.js
const cluster = require('cluster');
const app = require('./index.js');
const numCPUs = require('os').cpus().length;//cpu核心数
if (cluster.isMaster) {//不是主进程,则fork workers
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {//有子进程退出了则重新fork
console.log((new Date()).toLocaleString()+`:worker ${worker.process.pid} died,restart..` );
cluster.fork();
});
} else {
app();//启动app
}
执行node cluster.js
,查看进程,ps -ef | grep cluster.js
我们可以看到有4个子进程,OK!成功
501 18306 14421 0 4:23PM ttys001 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn cluster.js
501 18292 9652 0 4:22PM ttys005 0:00.30 node cluster.js
501 18293 18292 0 4:22PM ttys005 0:00.42 /usr/local/Cellar/node/4.2.1/bin/node /Users/weijianli/Work/tplServer_node/cluster.js
501 18294 18292 0 4:22PM ttys005 0:00.42 /usr/local/Cellar/node/4.2.1/bin/node /Users/weijianli/Work/tplServer_node/cluster.js
501 18295 18292 0 4:22PM ttys005 0:00.43 /usr/local/Cellar/node/4.2.1/bin/node /Users/weijianli/Work/tplServer_node/cluster.js
501 18296 18292 0 4:22PM ttys005 0:00.42 /usr/local/Cellar/node/4.2.1/bin/node /Users/weijianli/Work/tplServer_node/cluster.js
lsof -i:4500
我们看到,node占用了4500这个端口号
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 18292 weivea 21u IPv6 0x76cf94da255eb7e3 0t0 TCP *:ipsec-msft (LISTEN)
2、接下来再看看怎么实现守护进程
#!/bin/sh
WEB_DIR=`pwd;`
WEB_APP='cluster.js'
#location of node you want to use
NODE_EXE=/usr/local/bin/node
#就是这么霸气,循环执行,挂了就起,看了看,其实并不恐怖,只要node应用没挂,该shell就不会往下执行
while true; do
{
$NODE_EXE $WEB_DIR/$WEB_APP #执行node应用
DATE_S=`date;`
echo "$DATE_S:Stopped unexpected, restarting \r\n\r\n" >> $WEB_DIR/error.log
} 2>> $WEB_DIR/error.log
sleep 2s
done
3、还需要一个简单的启动和关闭方式
app.sh终于轮到它了;
用法:
开启应用:./app.sh start
关闭应用:./app.sh stop
说说逻辑:
首先,开启应用:首先检测是否存在应用进程和应用守护进程。
如果都存在,则kill调应用进程,此时守护进程会自动再次开启应用进程;
如果有一个不存在:则先kill掉存在的应用或守护进程,再启动守护进程,守护进程会启动应用进程。在启动守护进程的时候 用来nohup命令,所以,应用中的console.log()打印的信息会输出到./nohup.out中,对于懒人来说是不是有给你设了一道防火墙,
#!/bin/sh
WEB_DIR=`pwd;`
WEB_APP='cluster.js'
GUARD='guard.sh'
guardPID=`ps | grep $WEB_DIR/$GUARD | grep -v grep | head -n 1 | awk '{print $1}'`
pid=`ps | grep $WEB_DIR/$WEB_APP | grep -v grep | head -n 1 | awk '{print $1}'`
if [ $1 ] && [ $1 == 'start' ]
then
if [ $guardPID ] && [ $pid ]
then
DATE_S=`date;`
echo "restart:$DATE_S"
echo "restart:$DATE_S" >> $WEB_DIR/error.log
kill $pid
else
DATE_S=`date;`
echo "start:$DATE_S"
echo "start:$DATE_S" >> $WEB_DIR/error.log
if [ $guardPID ]
then
kill $guardPID
fi
if [ $pid ]
then
kill $pid
fi
nohup $WEB_DIR/$GUARD&
fi
elif [ $1 ] && [ $1 == 'stop' ]
then
DATE_S=`date;`
echo "stop:$DATE_S"
echo "stop:$DATE_S" >> $WEB_DIR/error.log
if [ $guardPID ]
then
kill $guardPID
fi
if [ $pid ]
then
kill $pid
fi
else
echo " use: $0 start or $0 stop"
fi
覆盖代码
sh app.sh start
开启应用,或重启应用
sh app.sh stop
关闭应用
最后,shell文件仅仅在mac下运行过,linux下不知道跑起来是啥样~