Home > Article > Web Front-end > An in-depth analysis of process management in NodeJS
Friends who are familiar with js know that js is single-threaded
. In Node, the model of multi-process single-threaded is adopted . Due to the single-thread limitation of JavaScript, on multi-core servers, we often need to start multiple processes to maximize server performance.
Node.js process clustering can be used to run multiple Node.js instances that can distribute workload among their application threads. When process isolation is not required, use the worker_threads
module instead, which allows multiple application threads to be run within a single Node.js instance.
Node introduced the cluster module after version V0.8, which implements clustering by
one master process (master) managing multiple child processes (workers).
The cluster module can easily create child processes that share server ports.
The bottom layer of cluster is the child_process module. In addition to sending ordinary messages, it can also send underlying objects
TCP
,UDP
, etc.,cluster
The module is a combined application of thechild_process
module and thenet
module. When the cluster starts, the TCP server will be started internally and the file descriptor of the TCP server-side socket will be sent to the worker process.
In the cluster
module application, a main process can only manage a group of worker processes
, and its operating mode does not have the child_process
module So flexible, but more stable:
const cluster = require('cluster')复
.isMaster
identifies the main process, Node.isPrimary
identifies the main process, Node>16.isWorker
Identifies the child process .worker
Reference to the current worker process object [in the child process] .workers
Storage The hash of the active worker process object, keyed by the id
field. This makes it easy to loop through all worker processes. It is only available in the main process. cluster.wokers[id] === worker
[In main process].settings
Read-only, cluster configuration items. After calling the .setupPrimary() or .fork() method, this settings object will contain the settings, including default values. Previously an empty object. This object should not be changed or set manually. cluster.settings
Configuration item details: - `execArgv` <string>传给 Node.js 可执行文件的字符串参数列表。 **默认值:** `process.execArgv`。 - `exec` <string> 工作进程文件的文件路径。 **默认值:** `process.argv[1]`。 - `args` <string> 传给工作进程的字符串参数。 **默认值:**`process.argv.slice(2)`。 - `cwd` <string>工作进程的当前工作目录。 **默认值:** `undefined` (从父进程继承)。 - `serialization` <string>指定用于在进程之间发送消息的序列化类型。 可能的值为 `'json'` 和 `'advanced'`。 **默认值:** `false`。 - `silent` <boolean>是否将输出发送到父进程的标准输入输出。 **默认值:** `false`。 - `stdio` <array>配置衍生进程的标准输入输出。 由于集群模块依赖 IPC 来运行,因此此配置必须包含 `'ipc'` 条目。 提供此选项时,它会覆盖 `silent`。 - `uid` <number>设置进程的用户标识。 - `gid` <number>设置进程的群组标识。 - `inspectPort` <number> | <function> 设置工作进程的检查器端口。 这可以是数字,也可以是不带参数并返回数字的函数。 默认情况下,每个工作进程都有自己的端口,从主进程的 `process.debugPort` 开始递增。 - `windowsHide` <boolean> 隐藏通常在 Windows 系统上创建的衍生进程控制台窗口。 **默认值:** `false`。</boolean></function></number></number></number></array></boolean></string></string></string></string></string>
Spawn a new worker process [in the main process]
Node>16
is used to change the default 'fork' behavior. After use, the settings will appear in
cluster.settings. Any setting changes will only affect future calls to
.fork(), not already running worker processes. The above default values only apply to the first call. Node is less than 16 [In the main process]
Called [In the main process] when all worker processes disconnect and close the handle
cluster module also exposes many events:
'message'
事件, 当集群主进程接收到来自任何工作进程的消息时触发。'exit'
事件, 当任何工作进程死亡时,则集群模块将触发 'exit'
事件。cluster.on('exit', (worker, code, signal) => { console.log('worker %d died (%s). restarting...', worker.process.pid, signal || code); cluster.fork(); });
'listening'
事件,从工作进程调用 listen()
后,当服务器上触发 'listening'
事件时,则主进程中的 cluster
也将触发 'listening'
事件。cluster.on('listening', (worker, address) => { console.log( `A worker is now connected to ${address.address}:${address.port}`); });
'fork'
事件,当新的工作进程被衍生时,则集群模块将触发 'fork'
事件。cluster.on('fork', (worker) => { timeouts[worker.id] = setTimeout(errorMsg, 2000); });
'setup'
事件,每次调用 .setupPrimary()
时触发。disconnect
事件,在工作进程 IPC 通道断开连接后触发。 当工作进程正常退出、被杀死、或手动断开连接时cluster.on('disconnect', (worker) => { console.log(`The worker #${worker.id} has disconnected`); });
Worker
对象包含了工作进程的所有公共的信息和方法。 在主进程中,可以使用 cluster.workers
来获取它。 在工作进程中,可以使用 cluster.worker
来获取它。
.id
工作进程标识,每个新的工作进程都被赋予了自己唯一的 id,此 id 存储在 id
。当工作进程存活时,这是在 cluster.workers
中索引它的键。.process
所有工作进程都是使用 child_process.fork()
创建,此函数返回的对象存储为 .process
。 在工作进程中,存储了全局的 process
。.send(message[, sendHandle[, options]][, callback])
向工作进程或主进程发送消息,可选择使用句柄。在主进程中,这会向特定的工作进程发送消息。 它与 ChildProcess.send()
相同。在工作进程中,这会向主进程发送消息。 它与 process.send()
相同。.destroy()
.kill([signal])
此函数会杀死工作进程。kill()
函数在不等待正常断开连接的情况下杀死工作进程,它与 worker.process.kill()
具有相同的行为。为了向后兼容,此方法别名为 worker.destroy()
。.disconnect([callback])
发送给工作进程,使其调用自身的 .disconnect()
将关闭所有服务器,等待那些服务器上的 'close'
事件,然后断开 IPC 通道。.isConnect()
如果工作进程通过其 IPC 通道连接到其主进程,则此函数返回 true
,否则返回 false
。 工作进程在创建后连接到其主进程。.isDead()
如果工作进程已终止(由于退出或收到信号),则此函数返回 true
。 否则,它返回 false
。为了让集群更加稳定和健壮,cluster
模块也暴露了许多事件:
'message'
事件, 在工作进程中。cluster.workers[id].on('message', messageHandler);
'exit'
事件, 当任何工作进程死亡时,则当前worker工作进程
对象将触发 'exit'
事件。if (cluster.isPrimary) { const worker = cluster.fork(); worker.on('exit', (code, signal) => { if (signal) { console.log(`worker was killed by signal: ${signal}`); } else if (code !== 0) { console.log(`worker exited with error code: ${code}`); } else { console.log('worker success!'); } }); }
'listening'
事件,从工作进程调用 listen()
,对当前工作进程进行监听。cluster.fork().on('listening', (address) => { // 工作进程正在监听 });
disconnect
事件,在工作进程 IPC 通道断开连接后触发。 当工作进程正常退出、被杀死、或手动断开连接时cluster.fork().on('disconnect', () => { //限定于当前worker对象触发 });
Node中主进程和子进程之间通过进程间通信 (IPC) 实现进程间的通信,进程间通过 .send()
(a.send表示向a发送)方法发送消息,监听 message
事件收取信息,这是 cluster模块
通过集成 EventEmitter
实现的。还是一个简单的官网的进程间通信例子
process.on('message')
、process.send()
child.on('message')
、child.send()
# cluster.isMaster # cluster.fork() # cluster.workers # cluster.workers[id].on('message', messageHandler); # cluster.workers[id].send(); # process.on('message', messageHandler); # process.send(); const cluster = require('cluster'); const http = require('http'); # 主进程 if (cluster.isMaster) { // Keep track of http requests console.log(`Primary ${process.pid} is running`); let numReqs = 0; // Count requests function messageHandler(msg) { if (msg.cmd && msg.cmd === 'notifyRequest') { numReqs += 1; } } // Start workers and listen for messages containing notifyRequest // 开启多进程(cpu核心数) // 衍生工作进程。 const numCPUs = require('os').cpus().length; for (let i = 0; i { console.log(`worker ${worker.process.pid} died`); }); } else { # 子进程 // 工作进程可以共享任何 TCP 连接 // 在本示例中,其是 HTTP 服务器 // Worker processes have a http server. http.Server((req, res) => { res.writeHead(200); res.end('hello world\n'); //****** !!!!Notify master about the request !!!!!!******* //****** 向process发送 process.send({ cmd: 'notifyRequest' }); //****** 监听从process来的 process.on('message', function(message) { // xxxxxxx }) }).listen(8000); console.log(`Worker ${process.pid} started`); }
NodeJS 进程之间通信只有消息传递,不会真正的传递对象。
send()
方法在发送消息前,会将消息组装成 handle 和 message,这个 message 会经过 JSON.stringify
序列化,也就是说,传递句柄的时候,不会将整个对象传递过去,在 IPC 通道传输的都是字符串,传输后通过 JSON.parse
还原成对象。
代码里有 app.listen(port)
在进行 fork 时,为什么多个进程可以监听同一个端口呢?
原因是主进程通过 send() 方法向多个子进程发送属于该主进程的一个服务对象的句柄,所以对于每一个子进程而言,它们在还原句柄之后,得到的服务对象是一样的,当网络请求向服务端发起时,进程服务是抢占式的,所以监听相同端口时不会引起异常。
# master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); for (let i=0; i<cpus.length><pre class="brush:php;toolbar:false"># worker.js const http = require('http'); http.createServer((req, res) => { res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid); }).listen(3000);
以上代码示例,控制台执行
node master.js
只有一个 worker 可以监听到 3000 端口,其余将会抛出Error: listen EADDRINUSE :::3000
错误。
发送句柄
功能/** * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * message * sendHandle */ subprocess.send(message, sendHandle)
当父子进程之间建立 IPC 通道之后,通过子进程对象的 send 方法发送消息,第二个参数 sendHandle 就是句柄,可以是 TCP套接字、TCP服务器、UDP套接字等
,为了解决上面多进程端口占用问题,我们将主进程的 socket 传递到子进程。
# master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); const server = require('net').createServer(); server.listen(3000); process.title = 'node-master' for (let i=0; i<cpus.length><pre class="brush:php;toolbar:false">// worker.js let worker; process.title = 'node-worker' process.on('message', function (message, sendHandle) { if (message === 'server') { worker = sendHandle; worker.on('connection', function (socket) { console.log('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid) }); } });
验证一番,控制台执行 node master.js
了解 cluster
的话会知道,子进程是通过 cluster.fork()
创建的。在 linux 中,系统原生提供了 fork
方法,那么为什么 Node 选择自己实现 cluster模块
,而不是直接使用系统原生的方法?主要的原因是以下两点:
fork的进程监听同一端口会导致端口占用错误
fork的进程之间没有负载均衡,容易导致惊群现象
在 cluster模块
中,针对第一个问题,通过判断当前进程是否为 master进程
,若是,则监听端口,若不是则表示为 fork 的 worker进程
,不监听端口。
针对第二个问题,cluster模块
内置了负载均衡功能, master进程
负责监听端口接收请求,然后通过调度算法(默认为 Round-Robin,可以通过环境变量 NODE_CLUSTER_SCHED_POLICY
修改调度算法)分配给对应的 worker进程
。
当代码抛出了异常没有被捕获到时,进程将会退出,此时 Node.js 提供了 process.on('uncaughtException', handler)
接口来捕获它,但是当一个 Worker 进程遇到未捕获的异常时,它已经处于一个不确定状态,此时我们应该让这个进程优雅退出:
+---------+ +---------+ | Worker | | Master | +---------+ +----+----+ | uncaughtException | +------------+ | | | | +---------+ | + ---------------------> | | wait... | | | exit | | +-------------------------> | | | | | die | | | | | |
当一个进程出现异常导致 crash 或者 OOM 被系统杀死时,不像未捕获异常发生时我们还有机会让进程继续执行,只能够让当前进程直接退出,Master 立刻 fork 一个新的 Worker。
child_process 模块提供了衍生子进程的能力, 简单来说就是执行cmd命令的能力
。
默认情况下, stdin、 stdout 和 stderr 的管道会在父 Node.js 进程和衍生的子进程之间建立
。 这些管道具有有限的(且平台特定的)容量。 如果子进程写入 stdout 时超出该限制且没有捕获输出,则子进程会阻塞并等待管道缓冲区接受更多的数据。 这与 shell 中的管道的行为相同。 如果不消费输出,则使用 { stdio: 'ignore' } 选项。
const cp = require('child_process');
通过 API 创建出来的子进程和父进程没有任何必然联系
4个异步方法,创建子进程:fork、exec、execFile、spawn
Node
fork(modulePath, args)
:想将一个 Node 进程作为一个独立的进程来运行的时候使用,使得计算处理和文件描述器脱离 Node 主进程(复制一个子进程)非 Node
spawn(command, args)
:处理一些会有很多子进程 I/O 时、进程会有大量输出时使用execFile(file, args[, callback])
:只需执行一个外部程序的时候使用,执行速度快,处理用户输入相对安全exec(command, options)
:想直接访问线程的 shell 命令时使用,一定要注意用户输入3个同步方法:execSync
、execFileSync
、spawnSync
其他三种方法都是 spawn()
的延伸。
记住,衍生的 Node.js 子进程独立于父进程,但两者之间建立的 IPC 通信通道除外。 每个进程都有自己的内存,带有自己的 V8 实例
举个?
在一个目录下新建 worker.js 和 master.js 两个文件:
# child.js const t = JSON.parse(process.argv[2]); console.error(`子进程 t=${JSON.stringify(t)}`); process.send({hello:`儿子pid=${process.pid} 给爸爸进程pid=${process.ppid} 请安`}); process.on('message', (msg)=>{ console.error(`子进程 msg=${JSON.stringify(msg)}`); });
# parent.js const {fork} = require('child_process'); for(let i = 0; i { console.log(`messsgae from child msg=${JSON.stringify(msg)}`, ); }); p.send({hello:`来自爸爸${process.pid} 进程id=${i}的问候`}); }
通过 node parent.js
启动 parent.js,然后通过 ps aux | grep worker.js
查看进程的数量,我们可以发现,理想状况下,进程的数量等于 CPU 的核心数,每个进程各自利用一个 CPU 核心。
这是经典的 Master-Worker 模式(主从模式)
实际上,fork 进程是昂贵的,复制进程的目的是充分利用 CPU 资源,所以 NodeJS 在单线程上使用了事件驱动的方式来解决高并发的问题。
适用场景
一般用于比较耗时的场景,并且用node去实现的,比如下载文件;
fork可以实现多线程下载:将文件分成多块,然后每个进程下载一部分,最后拼起来;
const cp = require('child_process'); // 第一个参数,要运行的可执行文件的名称或路径。这里是echo cp.execFile('echo', ['hello', 'world'], (err, stdout, stderr) => { if (err) { console.error(err); } console.log('stdout: ', stdout); console.log('stderr: ', stderr); });
适用场景
比较适合开销小的任务,更关注结果,比如ls等;
主要用来执行一个shell方法,其内部还是调用了spawn ,不过他有最大缓存限制。
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | sort | uniq`, (err, stdout, stderr) => { console.log(stdout); });
适用场景
比较适合开销小的任务,更关注结果,比如ls等;
单一任务
const cp = require('child_process'); const child = cp.spawn('echo', ['hello', 'world']); child.on('error', console.error); # 输出是流,输出到主进程stdout,控制台 child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
多任务串联
const cp = require('child_process'); const path = require('path'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); const sort = cp.spawn('sort'); const uniq = cp.spawn('uniq'); # 输出是流 cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout);
适用场景
spawn是流式的,所以适合耗时任务,比如执行npm install,打印install的过程
在进程已结束并且子进程的标准输入输出流(sdtio)已关闭之后,则触发 'close'
事件。这个事件跟exit
不同,因为多个进程可以共享同个stdio流。
参数:
Question: code must be Are there any?
(It seems not from the comments on code) For example, use kill
to kill the child process. So, what is the code?
Parameters:
code, signal, if the child process exits by itself, then code
is the exit code , otherwise null;
If the child process ends through a signal, then signal
is the signal to end the process, otherwise it is null.
Of the two, one must not be null.
Note:exit
When the event is triggered, the stdio stream of the child process may still be open. (Scenario?) In addition, nodejs listens to the SIGINT and SIGTERM signals. That is to say, when nodejs receives these two signals, it will not exit immediately. Instead, it will do some cleanup work first and then rethrow these two signals. (Visually, js can do cleanup work at this time, such as closing the database, etc.)
SIGINT
: interrupt, program termination signal, usually issued when the user presses CTRL C, used to notify The foreground process terminates the process. SIGTERM
: terminate, program end signal, this signal can be blocked and processed, and is usually used to require the program to exit normally. The shell command kill generates this signal by default. If the signal cannot be terminated, we will try SIGKILL (forced termination).
When the following things happen, error will be triggered. When error triggers, exit may or may not trigger. (My heart is broken)
Triggered when process.send()
is used to send a message.
Parameters: message
, which is a json object or primitive value; sendHandle
, which is a net.Socket object or net.Server Object (students who are familiar with cluster should be familiar with this)
.connected: When calling .disconnected()
, set is false. Represents whether it can receive messages from the child process or send messages to the child process.
.disconnect(): Close the IPC channel between the parent process and the child process. When this method is called, the disconnect
event will be triggered. If the child process is a node instance (created through child_process.fork()), then process.disconnect()
can also be actively called inside the child process to terminate the IPC channel.
To deal with single-threaded problems, multi-process methods are usually used to simulate multi-threading
The Node process occupies 7 threads
The core of Node is the v8 engine. After Node is started, an instance of v8 will be created. This instance is multi-threaded
single-threaded, but The hosting environment of Javascript, whether it is Node or the browser, is multi-threaded.
Why is Javascript single-threaded? This problem needs to start with the browser. For DOM operations in the browser environment, just imagine if multiple threads operate on the same DOM, will it be chaotic? That means that the DOM The operation can only be single-threaded to avoid DOM rendering conflicts. In the browser environment, the UI rendering thread and the JS execution engine are mutually exclusive. When one is executed, the other will be suspended. This is determined by the JS engine.
process.env.UV_THREADPOOL_SIZE = 64
worker_threads
给 Node 提供真正的多线程能力const { isMainThread, parentPort, workerData, threadId, MessageChannel, MessagePort, Worker } = require('worker_threads'); function mainThread() { for (let i = 0; i { console.log(`main: worker stopped with exit code ${code}`); }); worker.on('message', msg => { console.log(`main: receive ${msg}`); worker.postMessage(msg + 1); }); } } function workerThread() { console.log(`worker: workerDate ${workerData}`); parentPort.on('message', msg => { console.log(`worker: receive ${msg}`); }), parentPort.postMessage(workerData); } if (isMainThread) { mainThread(); } else { workerThread(); }
const assert = require('assert'); const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { const worker = new Worker(__filename); const subChannel = new MessageChannel(); worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message', (value) => { console.log('received:', value); }); } else { parentPort.once('message', (value) => { assert(value.hereIsYourPort instanceof MessagePort); value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.close(); }); }
进程是资源分配的最小单位,线程是CPU调度的最小单位
IPC (Inter-process communication) 即进程间通信
,由于每个进程创建之后都有自己的独立地址空间,实现 IPC 的目的就是为了进程之间资源共享访问。
实现 IPC 的方式有多种:管道、消息队列、信号量、Domain Socket,Node.js 通过 pipe 来实现。
实际上,父进程会在创建子进程之前,会先创建 IPC 通道并监听这个 IPC,然后再创建子进程,通过环境变量(NODE_CHANNEL_FD)告诉子进程和 IPC 通道相关的文件描述符,子进程启动的时候根据文件描述符连接 IPC 通道,从而和父进程建立连接。
句柄是一种可以用来标识资源的引用的,它的内部包含了指向对象的文件资源描述符。
一般情况下,当我们想要将多个进程监听到一个端口下,可能会考虑使用主进程代理的方式处理:
然而,这种代理方案会导致每次请求的接收和代理转发用掉两个文件描述符,而系统的文件描述符是有限的,这种方式会影响系统的扩展能力。
所以,为什么要使用句柄?原因是在实际应用场景下,建立 IPC 通信后可能会涉及到比较复杂的数据处理场景,句柄可以作为 send()
方法的第二个可选参数传入,也就是说可以直接将资源的标识通过 IPC 传输,避免了上面所说的代理转发造成的文件描述符的使用。
以下是支持发送的句柄类型:
父进程创建子进程之后,父进程退出了,但是父进程对应的一个或多个子进程还在运行,这些子进程会被系统的 init 进程收养,对应的进程 ppid 为 1,这就是孤儿进程。通过以下代码示例说明。
# worker.js const http = require('http'); const server = http.createServer((req, res) => { res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid); // 记录当前工作进程 pid 及父进程 ppid }); let worker; process.on('message', function (message, sendHandle) { if (message === 'server') { worker = sendHandle; worker.on('connection', function(socket) { server.emit('connection', socket); }); } });
# master.js const fork = require('child_process').fork; const server = require('net').createServer(); server.listen(3000); const worker = fork('worker.js'); worker.send('server', server); console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid); process.exit(0); // 创建子进程之后,主进程退出,此时创建的 worker 进程会成为孤儿进程
控制台进行测试,输出当前工作进程 pid 和 父进程 ppid
由于在 master.js 里退出了父进程,活动监视器所显示的也就只有工作进程。
再次验证,打开控制台调用接口,可以看到工作进程 5611 对应的 ppid 为 1(为 init 进程),此时已经成为了孤儿进程
守护进程运行在后台不受终端的影响,什么意思呢?
Node.js 开发的同学们可能熟悉,当我们打开终端执行 node app.js
开启一个服务进程之后,这个终端就会一直被占用,如果关掉终端,服务就会断掉,即前台运行模式
。
如果采用守护进程进程方式,这个终端我执行 node app.js
开启一个服务进程之后,我还可以在这个终端上做些别的事情,且不会相互影响。
创建子进程
在子进程中创建新会话(调用系统函数 setsid)
改变子进程工作目录(如:“/” 或 “/usr/ 等)
父进程终止
options.detached
为 true 可以使子进程在父进程退出后继续运行(系统层会调用 setsid 方法),这是第二步操作。// index.js const spawn = require('child_process').spawn; function startDaemon() { const daemon = spawn('node', ['daemon.js'], { cwd: '/usr', detached : true, stdio: 'ignore', }); console.log('守护进程开启 父进程 pid: %s, 守护进程 pid: %s', process.pid, daemon.pid); daemon.unref(); } startDaemon()
daemon.js 文件里处理逻辑开启一个定时器每 10 秒执行一次,使得这个资源不会退出,同时写入日志到子进程当前工作目录下
/usr/daemon.js const fs = require('fs'); const { Console } = require('console'); // custom simple logger const logger = new Console(fs.createWriteStream('./stdout.log'), fs.createWriteStream('./stderr.log')); setInterval(function() { logger.log('daemon pid: ', process.pid, ', ppid: ', process.ppid); }, 1000 * 10);
守护进程实现 Node.js 版本 源码地址
https://github.com/Q-Angelo/project-training/tree/master/nodejs/simple-daemon
在实际工作中对于守护进程并不陌生,例如 PM2、Egg-Cluster 等,以上只是一个简单的 Demo 对守护进程做了一个说明,在实际工作中对守护进程的健壮性要求还是很高的,例如:进程的异常监听、工作进程管理调度、进程挂掉之后重启等等,这些还需要去不断思考。
目录是什么?
进程的当前工作目录可以通过 process.cwd()
命令获取,默认为当前启动的目录,如果是创建子进程则继承于父进程的目录,可通过 process.chdir()
命令重置,例如通过 spawn 命令创建的子进程可以指定 cwd 选项设置子进程的工作目录。
有什么作用?
例如,通过 fs 读取文件,如果设置为相对路径则相对于当前进程启动的目录进行查找,所以,启动目录设置有误的情况下将无法得到正确的结果。还有一种情况程序里引用第三方模块也是根据当前进程启动的目录来进行查找的。
// 示例 process.chdir('/Users/may/Documents/test/') // 设置当前进程目录 console.log(process.cwd()); // 获取当前进程目录
更多node相关知识,请访问:nodejs 教程!
The above is the detailed content of An in-depth analysis of process management in NodeJS. For more information, please follow other related articles on the PHP Chinese website!