七天学会NodeJS(三)进程管理(util.format、Process、Child Process、Cluster)、子进程父进程之间通讯、异步编程、异常处理、域(Domain)
文章目录
总结:
进程管理
本章小结
- 使用
process
对象管理自身。- 使用
child_process
模块创建和管理子进程。util.format(format, […])
- 根据第一个参数,返回一个格式化字符串,类似
printf
的格式化输出。- 第一个参数是一个字符串,包含零个或多个占位符。 每一个占位符被替换为与其对应的转换后的值。 支持的占位符有:
%s
- 字符串.%d
- 数字 (整型和浮点型).%j
- JSON. 如果这个参数包含循环对象的引用,将会被替换成字符串'[Circular]'
。%%
- 单独一个百分号('%'
)。不会消耗一个参数。- 如果占位符没有相对应的参数,占位符将不会被替换。
Proces
官方文档: http://nodejs.org/api/process.html
在NodeJS中,可以通过
process
对象感知和控制NodeJS自身进程的方方面面。
process
不是内置模块,而是一个全局对象,因此在任何地方都可以直接使用。如何获取命令行参数
- 在NodeJS中可以通过
process.argv
获取命令行参数- 由于
argv[0]
固定等于NodeJS执行程序的绝对路径,argv[1]
固定等于主模块的绝对路径。- 第一个命令行参数从
argv[2]
开始如何退出程序
- 正常退出状态码=0
- 捕捉异常后退出 process.exit(1)
如何控制输入输出
- NodeJS程序的标准输入流(stdin)、一个标准输出流(stdout)、一个标准错误流(stderr)分别对应
process.stdin
、process.stdout
和process.stderr
,- 第一个是只读数据流,后边两个是只写数据流
子进程父进程之间通讯
/* parent.js 这是父进程*/ //父进程在创建子进程,在options.stdio字段中通过ipc开启了一条IPC通道,之后就可以监听子进程对象的message事件接收来自子进程的消息 var child = child_process.spawn('node', [ 'child.js' ], { stdio: [ 0, 1, 2, 'ipc' ] }); child.on('message', function (msg) { console.log(msg); }); //通过.send方法给子进程发送消息 child.send({ hello: 'hello' }); /* child.js 这是子进程*/ //在子进程这边,可以在process对象上监听message事件接收来自父进程的消息 process.on('message', function (msg) { msg.hello = msg.hello.toUpperCase(); //并通过.send方法向父进程发送消息。 process.send(msg); });
Child Process
- 官方文档: http://nodejs.org/api/child_process.html
- 使用
child_process
模块可以创建和控制子进程。- 该模块提供的API中最核心的是
.spawn
,其余API都是针对特定使用场景对它的进一步封装,算是一种语法糖。
- 使用
.spawn(exec, args, options)
方法,创建子进程,该方法支持三个参数。- 第一个参数是执行文件路径,可以是执行文件的相对或绝对路径,也可以是根据PATH环境变量能找到的执行文件名。
- 第二个参数中,数组中的每个成员都按顺序对应一个命令行参数。
- 第三个参数可选,用于配置子进程的执行环境与行为。
Cluster
cluster
模块是对child_process
模块的进一步封装,专用于解决单进程NodeJS Web服务器无法充分利用多核CPU的问题。- 使用该模块可以简化多进程服务器程序的开发,让每个核上运行一个工作进程,并统一通过主进程监听端口和分发请求。
异步编程
本章小结
- 不掌握异步编程就不算学会NodeJS。
- 异步编程依托于回调来实现,而使用回调不一定就是异步编程。
- 异步编程下的函数间数据传递、数组遍历和异常处理与同步编程有很大差别。
- 使用
domain
模块简化异步代码的异常处理,并小心陷阱。简介
- NodeJS最大的卖点——事件机制和异步IO,对开发者并不是透明的。
回调
你不知道用户何时单击按钮。 因此,为点击事件定义了一个事件处理程序。 该事件处理程序会接受一个函数,该函数会在该事件被触发时被调用:
document.getElementById('button').addEventListener('click', () => { //被点击 })
这就是所谓的回调。回调是一个简单的函数,会作为值被传给另一个函数,并且仅在事件发生时才被执行。
我们仍然回到JS是单线程运行的这个事实上,这决定了JS在执行完一段代码之前无法执行包括回调函数在内的别的代码。也就是说,即使平行线程完成工作了,通知JS主线程执行回调函数了,回调函数也要等到JS主线程空闲时才能开始执行。
异常处理
在NodeJS中,几乎所有异步API都按照以下方式设计,回调函数中第一个参数都是
err
。因此我们在编写自己的异步函数时,也可以按照这种方式来处理异常,与NodeJS的设计风格保持一致。function async(fn, callback) { // Code execution path breaks here. setTimeout(function () { try { callback(null, fn()); } catch (err) { callback(err); } }, 0); } async(null, function (err, data) { if (err) { console.log('Error: %s', err.message); } else { // Do something. } }); -- Console ------------------------------ Error: object is not a function
回调函数已经让代码变得复杂了,而异步方式下对异常的处理更加剧了代码的复杂度。如果NodeJS的最大卖点最后变成这个样子,那就没人愿意用NodeJS了,因此接下来会介绍NodeJS提供的一些解决方案。
域(Domain)
官方文档: http://nodejs.org/api/domain.html
可以简化异步代码的异常处理
为了让代码好看点,我们可以在每处理一个请求时,使用
domain
模块创建一个子域(JS子运行环境)。在子域内运行的代码可以随意抛出异常,而这些异常可以通过子域对象的error
事件统一捕获。function async(request, callback) { // Do something. asyncA(request, function (data) { // Do something asyncB(request, function (data) { // Do something asyncC(request, function (data) { // Do something callback(data); }); }); }); } http.createServer(function (request, response) { var d = domain.create(); d.on('error', function () { response.writeHead(500); response.end(); }); d.run(function () { async(request, function (data) { response.writeHead(200); response.end(data); }); }); });
1. 进程管理
NodeJS可以感知和控制自身进程的运行环境和状态,也可以创建子进程并与其协同工作,这使得NodeJS可以把多个程序组合在一起共同完成某项工作,并在其中充当胶水和调度器的作用。本章除了介绍与之相关的NodeJS内置模块外,还会重点介绍典型的使用场景。
开门红
我们已经知道了NodeJS自带的fs
模块比较基础,把一个目录里的所有文件和子目录都拷贝到另一个目录里需要写不少代码。另外我们也知道,终端下的cp
命令比较好用,一条cp -r source/* target
命令就能搞定目录拷贝。那我们首先看看如何使用NodeJS调用终端命令来简化目录拷贝,示例代码如下:
var child_process = require('child_process');
var util = require('util');
function copy(source, target, callback) {
child_proces