v11.10 nodejs 学习 (一)
学而时更之,不亦乐乎
版本:V11.10 LTS
Note: 内容主要以个人基础为起点,只记录了个人认为需要的东西。官网
Async Hooks
v8 新增。用于监测所有异步操作。目前处于实验阶段。
async_hooks.executionAsyncId()
返回当前环境上下文的 ID。
async_hooks.triggerAsyncId()
返回触发当前代码执行的环境上下文 ID。比如,在一个 Tick(环境 ID 假设为 1)中执行了异步操作 A,那么 A 的 triggerId 就是 1。
async_hooks.createHook(callbacks)
- callback: <对象>
创建一个 Hook 实例 hook,callback 是个回调对象,可包含 init/before/after/destroy/promiseResolve。__NOTE__:由于 console.log 在 Nodejs 中也是异步操作,因此不能在各个回调中使用 log 来打印信息,会无限循环。常见的采用写文件方式。关于 console.log 是异步的验证,如下代码:
1 | const asyncHooks = require('async_hooks'); |
会输出:
1 | true |
然后代码中去掉最后两行 log,输出:
1 | true |
发现,console.log 的调用过程存在异步的调用(关于 process.stdout 是异步还是同步,process 一节有说明,但我觉得不能解释这个 console.log 是异步 TODO)
根据文档及我的理解,executionAsyncId 为 0,是 C/C++ 环境上的上下文;1,是主程序的环境(及全局);之后递增的,便是走 event loop 的异步环境。triggerAsyncId 是触发这次异步操作的 executionAsyncId。
hook.enable() / disable()
开启或者关闭(在 create 后默认是关闭的)。
回调配置
before(async)
- asyncId: 数字
当异步操作初始化后或者完成时,将调用回调来通知用户。before 回调在所述回调执行前执行。before 回调可能被执行 0 次或 N 次。当异步操作被取消时,before 不会被调用;类似 TCP 服务器会调用 before 多次;像 fs.open 只会调用一次。
after(asyncId)
- asyncId: 数字
当通知用户的回调执行完成后立即执行。
destroy(asyncId)
当与 asyncId 相关的资源销毁时(依赖于 gc)被调用。它也会被内嵌的 API __emitDestroy ()__调用。
promiseResolve(asyncId)
当 Promise 的 resolve 调用时被调用。
Buffer
Buffer 为 Nodejs 提供了对二进制文件的操作。
关于构造函数
由于安全、易用性等,现已废除 new Buffer 构造器。使用 Buffer.alloc/from/allocUnsafe 等代替。
--zero-fill-buffers
开启每次生成 buffer 都清空数据。
关于 Buffer.from 是 copy 还是 share
当 Buffer.from 传入一个 TypedArray 时,会复制该 typedArray 的 content;当传入 typedArray.buffer 属性时,会 share 该 buffer。
遍历
可以只用 for…of,buf.keys (), buf.values (), buf.entries ()
Class: Buffer
new Buffer
所有参数形式的构造函数均废弃。采用 Buffer.from 替代。
Class Method: Buffer.alloc(size[, fill[, encoding]])
- size: buffer 大小
- fill:string|buffer|integer,默认为 0
- encoding:默认 utf8
如果指定了 fill 和 encoding,会在初始化后调用 buf.fill (fill, encoding) 来重置内存。
Class Method: Buffer.allocUnsafe(size)
基本跟上面那个一样,但是不会用 buf.fill (fill, encoding) 来重置内存。Buffer 内部有个预分配的内部 Buffer 实例,用于快速分配新的 Buffer 实例,前提是通过 Buffer.allowUnsafe 来创建。当大小小于 4k 时,会使用这个 pool。
Class Method: Buffer.allocUnsafeSlow(size)
不采用内部的 pool,可以长久保存(需要注意内存泄露的问题)。
Child Processes
默认上,stdin/stdout/stderr 的管道建立在 Nodejs 进程和衍生进程间。这些管道都是有限制的(通常还与平台有关),当子进程输出到 stdout 上超过了被消费的内容,子进程就会阻塞管道缓冲区接收更多数据。这与 shell 的管道行为一致。如果不消费输入,可以设置 **{stdio: ‘ignore’}**。
child_process.spawn () 会异步的产生子进程,不会阻塞 event loop;child_process.spawnSync () 同步的产生子进程,阻塞,直到进程退出或者被干掉。
Nodejs 提供了为数不多的同步和异步方法来代替 spawn/spawnSync。但是,其他 API 都是以 spawn/spawnSync 为基础实现的。
- child_process.exec: 产生一个 shell,并在这个 shell 中运行命令,当结束后将 stdout 和 stderr 传给回调函数。
- child_process.execFile: 与上者类似,但它直接执行命令而不是先生成一个 shell。
- child_process.fork: 产生一个新的 Nodejs 进程,调用指定的模块建立 IPC 通信通道,允许在父和子之间发送消息。
- child_process.execSync: 同步
- child_process.execFileSync: 同步
虽然同步看着很棒,但会极大地影响 event loop 的速度。
所有异步的 API,都会返回一个 ChildProcess 实例,该类实现了 EventEmitter,所以只需对实例监听事件即可。exec 和 execFile 额外支持一个可选的 callback 参数,当子进程终结时会回调。
windows 上衍生.bat .cmd 文件
在类 Unix 平台(Unix、Linux、MacOS 等)上,execFile 更高效,因为它不会产生一个 shell。在 windows 上,.bat 和.cmd 文件不能在没有终端的情况下靠它自己运行起来,因此 windows 上不能调用 exeFile。在 Windows 上需要使用 exec 或者 spawn(例子见官网)。
exec(command[, options][, callback])
- command: 字符串。参数以空格区分。
- options: 对象
- cwd: 工作目录。default: null
- env: 环境键值对。default: null
- encoding: 默认’utf8’
- shell: 字符串,执行命令的 shell。UNIX 上默认’/bin/sh’,windows: process.env.ComSpec
- timeout: 超时时间,默认 0
- maxBuffer: 在 stdout 或 stderr 上最大的数据量(字节),如果超出,子进程会被干掉,输出也会被截断。
- killSignal: 字符串或者数字。默认’SIGTERM’
- uid: 数字
- gid: 数字
- windowsHide: 布尔。隐藏 windows 子进程的输出框。默认:false
- callback: 当进城拉闸时调用
- error
- stdout
- stderr
- Returns: ChildProcess 实例
在生成的 shell 中运行 command 命令,传递给 exec 的命令由 shell 直接处理,以及一些特殊字符需要相应处理。
1 | exec('"/path/to/test file/test.sh" arg1 arg2'); |
如果传递了 callback 参数,它在子进程结束时会调用被(error, stdout, stderr)。成功时,error 为 null;有错误时,error 为 Error 实例。error.code 为退出状态码,error.signal 为被干掉时的信号。stdout/stderr 可能为字符串或者 buffer,取决于 options 中 encoding 的值。
如果运行超过 timeout,父进程会发送 SIGTERM(默认终结信号,可通过 killSignal 改变)来终结子进程。
child_process.execFile(file[, args][, options][, callback])
- file: path to file
- args: <string[]>
execFile 和 exec 很类似,但不会产生一个 shell。也因为如此,IO 的重定向、文件通配符等不支持。
child_process.fork(modulePath[, args][, options])
- modulePath: 子进程中需要运行的模块的路径
- args: <string[]>
- options:
- cwd
- detached: 布尔。子进程独立于父进程运行。详见下文。
- env
- execPath: 用于创建子进程的可执行文件路径
- execArgv: 字符串数组。default:process.execArgv
- silent: 布尔。如果 true,子进程的 stdin、stdout、stderr 都会 pipe 到父进程,否则会继承父进程。
- stdio: 当传递了该项,会覆盖 silent 选项。见下文
- windowsVerbatimArguments
- uid
- gid
- Returns: ChildProcess 实例
fork 的子进程实例有一些父子间进程内建的通信信道。fork 默认会使用父进程的 process.execPath 来产生新的 Node.js 实例。
child_process.spawn(command[, args][, options])
- command: 要执行的命令
- args: 字符串数组,
- options:
- cwd:
- env
- argv0: 设置 argv [0]。
- stdio:见下文
- detached
- uid
- gid
- shell
- windowsVerbatimArguments
- windowsHide
- Returns: ChildProcess 实例
options.detached
在 windows 上,设为 true,能够让子进程在父进程拉闸后继续运行。子进程有它自己的 console 窗口。一旦对一个子进程开启分离选项,就不能再关闭了。
在非 Windows 平台上,如果设为 true,子进程会成为一个新的进程组和会话的先导者。注意,父进程退出后,子进程可能会继续运行,而不管它们是否已分离。
默认情况下,父进程会等待分离的子进程退出。为了避免父进程等待子进程退出这种情况,可以在父进程中使用 subProcess.unref () (subProcess 是 ChildProcess 实例) 方法。原理是这样会让父进程的 event loop 排除子进程。能让父进程独立于子进程退出, 前提是父子进程间没有建立的 IPC 信道。
当使用这个选项来开启长期运行的进程,当父进程退出后,子进程不会保持在后台继续运行,除非提供了未连接到父进程的 stdio 配置。如果继承了父进程的 stdio,子进程将保持链接到控制终端。
options.stdio
该选项用于配置建立在父子进程间的管道。默认情况下,子进程的 stdin、stdout、stderr 都重定向到相应的 subprocess.stdin、stdout、stderr 流上。这等于设置 options.stdio 为 [‘pipe’, ‘pipe’, ‘pipe’]。
stdio 可能为以下值:
- ‘pipe’: 等于 [‘pipe’, ‘pipe’, ‘pipe’],为默认值。
- ‘ignore’: 等于 [‘ignore’, ‘ignore’, ‘ignore’]
- ‘inherit’: 等于 [‘inherit’, ‘inherit’, ‘inherit’] 或者 [0, 1, 2]
否则 options.stdio 就是一个数组。在 0,1,2 位置上可以为以下值:
- ‘pipe’ - 在父子进程间建立一个管道。管道的父端作为子进程对象的属性 subprocess.stdio[fd] 暴露到父进程。fds0-2 分别代表着 subprocess.stdin/stdout/stderr。
- ‘ipc’ - 在父子进程间建立传递信息、文件描述符的 IPC 通道。一个 ChildProcess 实例最多有一个 IPCstdio 文件描述符。设置为此项,会开启 subprocess.send () 方法。如果子进程是 Node.js 进程,IPC 通道的存在将启用 process.send () 和 process.disconnect () 方法,在子进程中也有 disconnect、message 事件产生。
不支持不使用 process.send () 来获取 IPC 通道 fd 或者对不是 Node.js 实例的子进程使用 IPC 通道。 - ‘ignore’ - 子进程会忽略 fd。由于 Node.js 会对子进程始终打开 fds [0-2],设为该值会让 Node.js 打开 /dev/null 并且链接到子进程 fd 上。
- ‘inherit’ - 传递到父进程,类似与父进程共用控制终端的输入、输出。
- <Stream> - 共享一个可读或可写的流,比如一个文本终端、文件、socket 或者一个到子进程的管道。
- 正整数 - 被作为 fd 解释。
- null, undefined - 使用默认值。在 0-2,使用’pipe’,3 及以上,使用’ignore’
需要注意的是,当 IPC 通道建立在父子进程间,并且子进程是 Node.js 进程,子进程以未引用的 IPC 通道启动,直到子进程对’disconnect’或者’message’事件注册了回调。这允许子进程在没有被开放的 IPC 通道保持开启时正常退出。