1. Node多进程模型
单进程的Node应用在处理高并发请求时,会出现阻塞,导致系统响应变慢,甚至崩溃的情况。针对这个问题,Node提供了多进程的解决方案。
1.1 多进程通信
Node的多进程通信可以通过IPC(Inter-Process Communication)实现,IPC是一种进程间的通信机制。Node提供了两种IPC通信方式:
1.1.1 Child Process
Node提供了Child Process模块,用于创建和管理子进程。创建子进程后,主进程和子进程之间可以通过IPC通信,如下所示:
const { spawn } = require('child_process');
const child = spawn('ls', ['-lh', '/usr']);
child.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
child.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
child.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
上述代码创建了一个ls命令的子进程,子进程输出的结果可以通过stdout事件获取到。同时,通过stderr事件可以获取子进程错误输出。
1.1.2 Cluster
Cluster模块是对Child Process模块的进一步封装,提供了一种简化多进程编程的方式。Cluster模块可以自动将一个Node进程复制成多个子进程,通过共享一个端口来实现负载均衡。
以下是Cluster模块使用的示例:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// 衍生工作进程
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
// 工作进程可以共享任何TCP连接
// 在这里它是一个 HTTP 服务器
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
1.2 多进程模式选择
在选择多进程模式之前,需要考虑系统的硬件配置、应用场景和具体业务流程等因素。
1.2.1 高可用型多进程
当一个Node应用需要在多个节点上运行,可以选择采用高可用型多进程模式。该模式需要一个负载均衡器,把请求转发到多个有相同功能的子进程中,从而提高应用的可用性和伸缩性。
以下是一个使用PM2部署多进程的示例:
pm2 start app.js -i max
pm2 list
该示例会将app.js模块启动多个子进程,i max表示启动的进程数量与CPU内核数相同。
1.2.2 CPU密集型多进程
当Node应用需要处理大量的CPU密集型操作时,可以选择采用CPU密集型多进程模式。该模式需要使用一种算法,将任务划分为多个小任务,在多个子进程中分别处理,最后把结果汇总得到最终结果。
以下是一个使用Child Process多进程处理CPU密集型任务的示例:
const { spawn } = require('child_process');
const { cpus } = require('os');
const workers = [];
for (let i = 0; i < cpus().length; i++) {
const worker = spawn('node', ['worker.js']);
workers.push(worker);
}
// IPC
workers.forEach((worker) => {
worker.send('start');
});
该示例会启动与CPU内核数相同的子进程,每个子进程都执行worker.js模块中的代码,最后通过IPC通信获取计算结果。
2. 项目部署
在Node应用部署过程中,需要注意以下几个方面:
2.1 环境配置
在部署Node应用之前,需要在目标机器上安装Node环境,并保证版本一致。同时,还需要配置应用所需的数据库、缓存和消息队列等服务的环境变量。
以下是一个使用Docker部署Node应用的示例:
FROM node:10
# 设置环境变量
ENV MONGO_DB_USERNAME=admin
ENV MONGO_DB_PASSWORD=admin
ENV MONGO_DB_HOST=localhost
ENV MONGO_DB_PORT=27017
ENV MONGO_DB_NAME=mydb
# 在容器中创建应用目录
RUN mkdir -p /usr/src/app
# 设置工作目录
WORKDIR /usr/src/app
# 安装依赖
COPY package*.json ./
RUN npm install
# 拷贝源码到容器中
COPY . .
# 对外发布端口
EXPOSE 3000
# 启动应用
CMD [ "npm", "start" ]
2.2 远程部署
在远程部署Node应用时,可以使用SSH协议来进行远程连接,并使用rsync或scp等工具来进行文件传输。
以下是一个使用SSH连接并使用rsync部署Node应用的示例:
#!/bin/sh
# 目标服务器IP
IP="10.10.10.10"
# 应用目录
APP_DIR="/usr/local/app"
REMOTE_DIR="$IP:$APP_DIR"
# 部署
rsync -ravzc --progress ./ $REMOTE_DIR
# 安装依赖
ssh $IP "cd $APP_DIR && npm install"
# 启动应用
ssh $IP "cd $APP_DIR && pm2 start app.js -i max"
2.3 自动化部署
对于频繁修改的Node应用,可以使用自动化工具来实现快速部署。例如,可以使用Jenkins来实现源码编译、测试和自动部署等功能。
以下是一个使用Jenkins实现自动化构建和部署的示例:
node {
stage('Git Clone') {
git 'https://github.com//.git'
}
stage('Install Dependencies') {
sh 'npm install'
}
stage('Build') {
sh 'npm run build'
}
stage('Deploy') {
sshPublisher(
continueOnError: false,
failOnError: true,
publishers: [
sshPublisherDesc(
configName: "ssh-config",
verbose: true,
transfers: [
sshTransfer(
sourceFiles: "dist/**/*",
excludes: "",
remoteDirectory: "/usr/local/app"
)
]
)
]
)
}
}
该示例会从Git仓库中下载代码,安装依赖,构建应用,并通过SSH连接将构建结果传输到目标服务器。
3. 总结
Node多进程模型和项目部署是Node应用开发过程中的两个重要环节。Node提供了多种多进程编程的解决方案,我们需要根据具体业务需要进行选择。而在部署Node应用时,需要关注环境配置、远程部署和自动化部署等问题。