笔者近几月某公司WiFi测试平台项目中,了解到他们测试WiFi一直是人工手动测试,因此在老师提议下,决定采用puppeteer去实现页面数据的爬取和更改的自动化,在实施过程中,发现--路由管理页面--登陆后只有cookie加密,并且页面的数据和操作通过向同一接口传递不同参数实现,因此可以通过promise-request方法快速的实现上述功能。
本文主要记录、总结:
- node.js子进程的创建:node.js:child_process(官方文档)
- fork和spawn的区别与联系
- Node原生支持实现进程间通信
- WebSocket实现进程间通信
1. node.js子进程的创建
node创建子进程的方法可以参考上述官方文档,里面有详细的配置和说明。在本文中,笔者着重介绍笔者使用到的child_process.spawn()
方法和child_process.fork()
方法。
child_process.spawn()
方法:
Master进程代码:const child_process = require('child_process');//引入child_process模块 const {recLog} = require("./utils/log");//由于执行父进程,子进程无法在控制台打印信息,因此封装了log4js方法。 const gChildProcessNumber = 3;//创建子进程数量-全局 creatProcess(); async function creatProcess() { for (let i = 0; i < gChildProcessNumber; i += 1) { const workerProcess = child_process.spawn('node', ['./Request_Test/ChildProcess.js']); } }
Child进程代码:
const {recLog} = require("./utils/log"); recLog(`子进程创建成功,执行ChildProcess.js文件,进程pid为:${process.pid}`)
执行结果:
注意:这里使用child_process.spawn()
方法,由于笔者子进程运行的文件为js文件,因此直接用node运行,如果运行的是其他文件,比如笔者之前运行的×××.e2e.js文件时,笔者运行指令为npm test ×××.e2e.js,就需要写成如下:const workerProcess = child_process.spawn('npm.cmd', ['test', './Request_Test/request.e2e.js'], { });
因为在Windows上,当我们执行npm 时,我们实际执行的是npm. cnd批处理,所以一定要显示的执行npm.cmd否则会出现
Error: spawn npm ENOENT
的错误。child_process.fork()
方法:
Master进程代码:const child_process = require('child_process'); const {recLog} = require("./utils/log"); const gChildProcessNumber = 3; creatProcess(); async function creatProcess() { for (let i = 0; i < gChildProcessNumber; i += 1) { const workerProcess = child_process.fork('./Request_Test/ipcChildProcess.js', { silent: true, }); } }
Child进程代码:
const {recLog} = require("./utils/log"); recLog('子进程Ready!')
执行结果如下:
2. fork和spawn的区别与联系
Spawn是一个旨在运行系统命令的命令。当您运行spawn时,您将向其发送一个系统命令,该命令将在其自己的进程上运行,但不会在节点进程中执行任何其他代码。您可以为已生成的进程添加侦听器,以允许您的代码与生成的进程交互,但不会创建新的V8实例(除非当然您的命令是另一个Node命令,但在这种情况下应该使用fork!)和在处理器上只有一个节点模块副本处于活动状态。
Fork是一个特殊的spawn实例,它运行一个新的V8引擎实例。意思是,您可以创建多个工作程序,在完全相同的Node代码库上运行,或者为特定任务使用不同的模块。这对创建工作池最有用。虽然节点异步事件模型允许机器的单个核心被相当有效地使用,但它不允许节点进程使用多核机器。最简单的方法是在单个处理器上运行相同程序的多个副本。
下面我们在看看官方文档中,对fork的解释:
child_process.fork() 方法是 child_process.spawn() 的特例,专门用于衍生新的 Node.js 进程。 与 child_process.spawn() 一样,返回 ChildProcess 对象。 返回的 ChildProcess 将有额外的内置通信通道,允许消息在父进程和子进程之间来回传递。 详见 subprocess.send()。
请记住,衍生的 Node.js 子进程独立于父进程,除了两者之间建立的 IPC 通信通道。 每个进程都有自己的内存,具有自己的 V8 实例。 由于需要额外的资源分配,不建议衍生大量子 Node.js 进程。
综上所述:
- child_process.fork() 方法是特殊的 child_process.spawn()方法,即spawn()方法包含了fork()方法。
- child_process.fork() 方法默认运行node指令创建进程,该方法创建的子进程生成时便与父进程创建了IPC通信通道,且每个由该方法创建的子进程都具有自己的内存和V8实例。
3. Node原生实现进程间通信
Master进程代码:
const child_process = require('child_process');
const {recLog} = require("./utils/log");
const gChildProcessNumber = 3;
creatProcess();
async function creatProcess() {
for (let i = 0; i < gChildProcessNumber; i += 1) {
const workerProcess = child_process.fork('./Request_Test/ipcChildProcess.js', {
silent: true,
});
workerProcess.on('message', (Info) => {
recLog(`父进程收到消息-state:${Info.state}`)
workerProcess.send({command: 'init'});
})
}
}
Child进程代码:
const {recLog} = require("./utils/log");
process.send({state: 'action'});
recLog(`子进程${process.pid}发送action请求。`)
process.on("message", async (mess) => {
recLog(`子进程${process.pid}收到消息-command:${mess.command}`)
})
Node原生支持通信结果:
4. WebSocket实现进程间通信
Master进程代码:
const child_process = require('child_process');
const ws = require('nodejs-websocket')
const {recLog} = require("./utils/log");
const gChildProcessNumber = 3;
creatProcess();
function OpenWebSocketServer(port) {
ws.createServer((conn) => {
conn.on('text', (data) => { // 收到客户端数据的回调方法
recLog(`父进程收到子进程消息:${data}`);
switch (data) {
case 'connected':
recLog('进程间通信链接建立成功!')
conn.sendText('init')
break;
default:
recLog(`父进程收到未定义进程间消息:${data}!`)
break;
}
})
conn.on('connection', (connection) => {
recLog(connection)
})
conn.on('close', (e) => { // 关闭服务器的回调方法
recLog(`${e}服务链接关闭`)
})
conn.on('error', (e) => { // 服务端连接异常的回调方法
recLog(`${e}服务端异常`)
})
}).listen(port) // 监听8181端口,跟客户端连接端口对应
recLog(`${port}号端口,服务端已开启`)
}
async function creatProcess() {
for (let i = 0; i < gChildProcessNumber; i += 1) {
const workerProcess = child_process.spawn('node', ['./Request_Test/WebSocketChildProcess.js']);
OpenWebSocketServer(workerProcess.pid)
}
}
Child进程代码:
const request = require('request');
const {recLog} = require("./utils/log");
global.WebSocket = require('ws');
const ws = new WebSocket(`ws://localhost:${process.pid}`)
ws.onopen = function () {
recLog('连接服务器成功')
ws.send('connected')
}
ws.onmessage = function (e) {
recLog(`子进程收到父进程消息:${e.data}`)
const {data} = e;
switch (data){
case 'init':
recLog(`子进程收到init指令,执行init操作!`)
break;
default:
recLog('子进程未收到')
}
}
ws.onclose = function () {
recLog('连接已关闭')
}
WebSocket通信结果如下:
补充:笔者在新的项目中遇到了需要模拟CMD按序执行一系列指令的方法,特此补充,有需要自取!
var nodeCmd = require('node-cmd');
const testCommand = ['cd C:\\Users\\Administrator\\Desktop\\teaching-platform-node-mpi\\triedges\\m02a0', 'mpiexec -n 4 ./mflowMPI_v40.5.exe', 'forceget_v40.5.exe'];
let commandStr = '';
commandStr = testCommand.join(' && ');
console.log(commandStr)
function runCmdTest() {
nodeCmd.run(
commandStr,
function (err, data, stderr) {
recLog(data)
}
);
}
runCmdTest()
参考文档:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。