Node.js進(jìn)階之進(jìn)程與線程

進(jìn)程與線程在服務(wù)端研發(fā)中是一個(gè)非常重要的概念,如果您在學(xué)習(xí)的時(shí)候?qū)@一塊感到混亂或者不是太理解,可以閱讀下本篇內(nèi)容,本篇在介紹進(jìn)程和線程的概念之外,列舉了很多 Demo 希望能從實(shí)戰(zhàn)角度幫助您更好的去理解。

作者簡(jiǎn)介:五月君,Nodejs Developer,熱愛技術(shù)、喜歡分享的 90 后青年,公眾號(hào) “Nodejs技術(shù)?!保珿ithub 開源項(xiàng)目 https://www.nodejs.red

快速導(dǎo)航

進(jìn)程

進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),進(jìn)程是線程的容器(來自百科)。我們啟動(dòng)一個(gè)服務(wù)、運(yùn)行一個(gè)實(shí)例,就是開一個(gè)服務(wù)進(jìn)程,例如 Java 里的 JVM 本身就是一個(gè)進(jìn)程,Node.js 里通過 node app.js 開啟一個(gè)服務(wù)進(jìn)程,多進(jìn)程就是進(jìn)程的復(fù)制(fork),fork 出來的每個(gè)進(jìn)程都擁有自己的獨(dú)立空間地址、數(shù)據(jù)棧,一個(gè)進(jìn)程無法訪問另外一個(gè)進(jìn)程里定義的變量、數(shù)據(jù)結(jié)構(gòu),只有建立了 IPC 通信,進(jìn)程之間才可數(shù)據(jù)共享。

關(guān)于進(jìn)程通過一個(gè)簡(jiǎn)單的 Node.js Demo 來驗(yàn)證下,執(zhí)行以下代碼 node process.js,開啟一個(gè)服務(wù)進(jìn)程

// process.js
const http = require('http');

http.createServer().listen(3000, () => {
    process.title = '測(cè)試進(jìn)程 Node.js' // 進(jìn)程進(jìn)行命名
    console.log(`process.pid: `, process.pid); // process.pid: 20279
});

以下為 Mac 系統(tǒng)自帶的監(jiān)控工具 “活動(dòng)監(jiān)視器” 所展示的效果,可以看到我們剛開啟的 Nodejs 進(jìn)程 20279

圖片描述

線程

線程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位,首先我們要清楚線程是隸屬于進(jìn)程的,被包含于進(jìn)程之中。一個(gè)線程只能隸屬于一個(gè)進(jìn)程,但是一個(gè)進(jìn)程是可以擁有多個(gè)線程的。

同一塊代碼,可以根據(jù)系統(tǒng)CPU核心數(shù)啟動(dòng)多個(gè)進(jìn)程,每個(gè)進(jìn)程都有屬于自己的獨(dú)立運(yùn)行空間,進(jìn)程之間是不相互影響的。同一進(jìn)程中的多條線程將共享該進(jìn)程中的全部系統(tǒng)資源,如虛擬地址空間,文件描述符和信號(hào)處理等。但同一進(jìn)程中的多個(gè)線程有各自的調(diào)用棧(call stack),自己的寄存器環(huán)境(register context),自己的線程本地存儲(chǔ)(thread-local storage),線程又有單線程和多線程之分,具有代表性的 JavaScript、Java 語言。

單線程

單線程就是一個(gè)進(jìn)程只開一個(gè)線程,想象一下一個(gè)癡情的少年,對(duì)一個(gè)妹子一心一意用情專一。

Javascript 就是屬于單線程,程序順序執(zhí)行,可以想象一下隊(duì)列,前面一個(gè)執(zhí)行完之后,后面才可以執(zhí)行,當(dāng)你在使用單線程語言編碼時(shí)切勿有過多耗時(shí)的同步操作,否則線程會(huì)造成阻塞,導(dǎo)致后續(xù)響應(yīng)無法處理。你如果采用 Javascript 進(jìn)行編碼時(shí)候,請(qǐng)盡可能的使用異步操作。

一個(gè)計(jì)算耗時(shí)造成線程阻塞的例子

先看一段例子,運(yùn)行下面程序,瀏覽器執(zhí)行 http://127.0.0.1:3000/compute 大約每次需要 15657.310ms,也就意味下次用戶請(qǐng)求需要等待 15657.310ms,下文 Node.js 進(jìn)程創(chuàng)建一節(jié) 將采用 child_process.fork 實(shí)現(xiàn)多個(gè)進(jìn)程來處理。

// compute.js
const http = require('http');
const [url, port] = ['127.0.0.1', 3000];

const computation = () => {
    let sum = 0;
    console.info('計(jì)算開始');
    console.time('計(jì)算耗時(shí)');

    for (let i = 0; i < 1e10; i++) {
        sum += i
    };

    console.info('計(jì)算結(jié)束');
    console.timeEnd('計(jì)算耗時(shí)');
    return sum;
};

const server = http.createServer((req, res) => {
    if(req.url == '/compute'){
        const sum = computation();

        res.end(`Sum is ${sum}`);
    }

    res.end(`ok`);
});

server.listen(port, url, () => {
    console.log(`server started at http://${url}:${port}`);
});

單線程使用總結(jié)

  • Node.js 雖然是單線程模型,但是其基于事件驅(qū)動(dòng)、異步非阻塞模式,可以應(yīng)用于高并發(fā)場(chǎng)景,避免了線程創(chuàng)建、線程之間上下文切換所產(chǎn)生的資源開銷。
  • 如果你有需要大量計(jì)算,CPU 耗時(shí)的操作,開發(fā)時(shí)候要注意。

多線程

多線程就是沒有一個(gè)進(jìn)程只開一個(gè)線程的限制,好比一個(gè)風(fēng)流少年除了愛慕自己班的某個(gè)妹子,還在想著隔壁班的漂亮妹子。Java 就是多線程編程語言的一種,可以有效避免代碼阻塞導(dǎo)致的后續(xù)請(qǐng)求無法處理。

對(duì)于多線程的說明 Java 是一個(gè)很好的例子,看以下代碼示例,我將 count 定義在全局變量,如果定義在 test 方法里,又會(huì)輸出什么呢?

public class TestApplication {
    Integer count = 0;

    @GetMapping("/test")
    public Integer Test() {
        count += 1;
        return count;
    }

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

運(yùn)行結(jié)果,每次執(zhí)行都會(huì)修改count值,所以,多線程中任何一個(gè)變量都可以被任何一個(gè)線程所修改。

1 # 第一次執(zhí)行
2 # 第二次執(zhí)行
3 # 第三次執(zhí)行

我現(xiàn)在對(duì)上述代碼做下修改將 count 定義在 test 方法里

public class TestApplication {
    @GetMapping("/test")
    public Integer Test() {
        Integer count = 0; // 改變定義位置
        count += 1;
        return count;
    }

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

運(yùn)行結(jié)果如下所示,每次都是 1,因?yàn)槊總€(gè)線程都擁有了自己的執(zhí)行棧

1 # 第一次執(zhí)行
1 # 第二次執(zhí)行
1 # 第三次執(zhí)行

多線程使用總結(jié)

多線程的代價(jià)還在于創(chuàng)建新的線程和執(zhí)行期上下文線程的切換開銷,由于每創(chuàng)建一個(gè)線程就會(huì)占用一定的內(nèi)存,當(dāng)應(yīng)用程序并發(fā)大了之后,內(nèi)存將會(huì)很快耗盡。類似于上面單線程模型中例舉的例子,需要一定的計(jì)算會(huì)造成當(dāng)前線程阻塞的,還是推薦使用多線程來處理,關(guān)于線程與進(jìn)程的理解推薦閱讀下 阮一峰:進(jìn)程與線程的一個(gè)簡(jiǎn)單解釋。

Nodejs的線程與進(jìn)程

Node.js 是 Javascript 在服務(wù)端的運(yùn)行環(huán)境,構(gòu)建在 chrome 的 V8 引擎之上,基于事件驅(qū)動(dòng)、非阻塞I/O模型,充分利用操作系統(tǒng)提供的異步 I/O 進(jìn)行多任務(wù)的執(zhí)行,適合于 I/O 密集型的應(yīng)用場(chǎng)景,因?yàn)楫惒?,程序無需阻塞等待結(jié)果返回,而是基于回調(diào)通知的機(jī)制,原本同步模式等待的時(shí)間,則可以用來處理其它任務(wù),在 Web 服務(wù)器方面,著名的 Nginx 也是采用此模式(事件驅(qū)動(dòng)),Nginx 采用 C 語言進(jìn)行編寫,主要用來做高性能的 Web 服務(wù)器,不適合做業(yè)務(wù)。Web業(yè)務(wù)開發(fā)中,如果你有高并發(fā)應(yīng)用場(chǎng)景那么 Node.js 會(huì)是你不錯(cuò)的選擇。

在單核 CPU 系統(tǒng)之上我們采用 單進(jìn)程 + 單線程 的模式來開發(fā)。在多核 CPU 系統(tǒng)之上,可以通過 child_process.fork 開啟多個(gè)進(jìn)程(Node.js 在 v0.8 版本之后新增了Cluster 來實(shí)現(xiàn)多進(jìn)程架構(gòu)) ,即 多進(jìn)程 + 單線程 模式。注意:開啟多進(jìn)程不是為了解決高并發(fā),主要是解決了單進(jìn)程模式下 Node.js CPU 利用率不足的情況,充分利用多核 CPU 的性能。

Process

Node.js 中的進(jìn)程 Process 是一個(gè)全局對(duì)象,無需 require 直接使用,給我們提供了當(dāng)前進(jìn)程中的相關(guān)信息。官方文檔提供了詳細(xì)的說明,感興趣的可以親自實(shí)踐下 Process 文檔。

  • process.env:環(huán)境變量,例如通過 process.env.NODE_ENV 獲取不同環(huán)境項(xiàng)目配置信息
  • process.nextTick:這個(gè)在談及 Event Loop 時(shí)經(jīng)常為會(huì)提到
  • process.pid:獲取當(dāng)前進(jìn)程id
  • process.ppid:當(dāng)前進(jìn)程對(duì)應(yīng)的父進(jìn)程
  • process.cwd():獲取當(dāng)前進(jìn)程工作目錄
  • process.platform:獲取當(dāng)前進(jìn)程運(yùn)行的操作系統(tǒng)平臺(tái)
  • process.uptime():當(dāng)前進(jìn)程已運(yùn)行時(shí)間,例如:pm2 守護(hù)進(jìn)程的 uptime 值
  • 進(jìn)程事件:process.on('uncaughtException', cb) 捕獲異常信息、process.on('exit', cb)進(jìn)程推出監(jiān)聽
  • 三個(gè)標(biāo)準(zhǔn)流:process.stdout 標(biāo)準(zhǔn)輸出、process.stdin 標(biāo)準(zhǔn)輸入、process.stderr 標(biāo)準(zhǔn)錯(cuò)誤輸出

以上僅列舉了部分常用到功能點(diǎn),除了 Process 之外 Node.js 還提供了 child_process 模塊用來對(duì)子進(jìn)程進(jìn)行操作,在下文 Nodejs進(jìn)程創(chuàng)建一節(jié) 會(huì)講述。

關(guān)于 Node.js 進(jìn)程的幾點(diǎn)總結(jié)

  • Javascript 是單線程,但是做為宿主環(huán)境的 Node.js 并非是單線程的。
  • 由于單線程原故,一些復(fù)雜的、消耗 CPU 資源的任務(wù)建議不要交給 Node.js 來處理,當(dāng)你的業(yè)務(wù)需要一些大量計(jì)算、視頻編碼解碼等 CPU 密集型的任務(wù),可以采用 C 語言。
  • Node.js 和 Nginx 均采用事件驅(qū)動(dòng)方式,避免了多線程的線程創(chuàng)建、線程上下文切換的開銷。如果你的業(yè)務(wù)大多是基于 I/O 操作,那么你可以選擇 Node.js 來開發(fā)。

Nodejs進(jìn)程創(chuàng)建

Node.js 提供了 child_process 內(nèi)置模塊,用于創(chuàng)建子進(jìn)程,更多詳細(xì)信息可參考 Node.js 中文網(wǎng) child_process

四種方式

  • child_process.spawn():適用于返回大量數(shù)據(jù),例如圖像處理,二進(jìn)制數(shù)據(jù)處理。
  • child_process.exec():適用于小量數(shù)據(jù),maxBuffer 默認(rèn)值為 200 * 1024 超出這個(gè)默認(rèn)值將會(huì)導(dǎo)致程序崩潰,數(shù)據(jù)量過大可采用 spawn。
  • child_process.execFile():類似 child_process.exec(),區(qū)別是不能通過 shell 來執(zhí)行,不支持像 I/O 重定向和文件查找這樣的行為
  • child_process.fork(): 衍生新的進(jìn)程,進(jìn)程之間是相互獨(dú)立的,每個(gè)進(jìn)程都有自己的 V8 實(shí)例、內(nèi)存,系統(tǒng)資源是有限的,不建議衍生太多的子進(jìn)程出來,通長(zhǎng)根據(jù)系統(tǒng) CPU 核心數(shù)設(shè)置。

方式一:spawn

child_process.spawn(command[, args][, options])

創(chuàng)建父子進(jìn)程間通信的三種方式:

  • 讓子進(jìn)程的stdio和當(dāng)前進(jìn)程的stdio之間建立管道鏈接 child.stdout.pipe(process.stdout);
  • 父進(jìn)程子進(jìn)程之間共用stdio
  • 事件監(jiān)聽
const spawn = require('child_process').spawn;
const child = spawn('ls', ['-l'], { cwd: '/usr' }) // cwd 指定子進(jìn)程的工作目錄,默認(rèn)當(dāng)前目錄

child.stdout.pipe(process.stdout);
console.log(process.pid, child.pid); // 主進(jìn)程id3243 子進(jìn)程3244

方式二:exec

const exec = require('child_process').exec;

exec(`node -v`, (error, stdout, stderr) => {
    console.log({ error, stdout, stderr })
    // { error: null, stdout: 'v8.5.0\n', stderr: '' }
})

方式三:execFile

const execFile = require('child_process').execFile;

execFile(`node`, ['-v'], (error, stdout, stderr) => {
    console.log({ error, stdout, stderr })
    // { error: null, stdout: 'v8.5.0\n', stderr: '' }
})

方式四:fork

const fork = require('child_process').fork;
fork('./worker.js'); // fork 一個(gè)新的子進(jìn)程

fork子進(jìn)程充分利用CPU資源

上文單線程一節(jié) 例子中,當(dāng) CPU 計(jì)算密度大的情況程序會(huì)造成阻塞導(dǎo)致后續(xù)請(qǐng)求需要等待,下面采用 child_process.fork 方法,在進(jìn)行 cpmpute 計(jì)算時(shí)創(chuàng)建子進(jìn)程,子進(jìn)程計(jì)算完成通過 send 方法將結(jié)果發(fā)送給主進(jìn)程,主進(jìn)程通過 message 監(jiān)聽到信息后處理并退出。

fork_app.js

const http = require('http');
const fork = require('child_process').fork;

const server = http.createServer((req, res) => {
    if(req.url == '/compute'){
        const compute = fork('./fork_compute.js');
        compute.send('開啟一個(gè)新的子進(jìn)程');

        // 當(dāng)一個(gè)子進(jìn)程使用 process.send() 發(fā)送消息時(shí)會(huì)觸發(fā) 'message' 事件
        compute.on('message', sum => {
            res.end(`Sum is ${sum}`);
            compute.kill();
        });

        // 子進(jìn)程監(jiān)聽到一些錯(cuò)誤消息退出
        compute.on('close', (code, signal) => {
            console.log(`收到close事件,子進(jìn)程收到信號(hào) ${signal} 而終止,退出碼 ${code}`);
            compute.kill();
        })
    }else{
        res.end(`ok`);
    }
});

server.listen(3000, 127.0.0.1, () => {
    console.log(`server started at http://${127.0.0.1}:${3000}`);
});

fork_compute.js

針對(duì) 上文單線程一節(jié) 的例子需要進(jìn)行計(jì)算的部分拆分出來單獨(dú)進(jìn)行運(yùn)算。

const computation = () => {
    let sum = 0;
    console.info('計(jì)算開始');
    console.time('計(jì)算耗時(shí)');

    for (let i = 0; i < 1e10; i++) {
        sum += i
    };

    console.info('計(jì)算結(jié)束');
    console.timeEnd('計(jì)算耗時(shí)');
    return sum;
};

process.on('message', msg => {
    console.log(msg, 'process.pid', process.pid); // 子進(jìn)程id
    const sum = computation();

    // 如果Node.js進(jìn)程是通過進(jìn)程間通信產(chǎn)生的,那么,process.send()方法可以用來給父進(jìn)程發(fā)送消息
    process.send(sum);
})

Nodejs多進(jìn)程架構(gòu)模型

多進(jìn)程架構(gòu)解決了單進(jìn)程、單線程無法充分利用系統(tǒng)多核 CPU 的問題,通過上文對(duì) Node.js 進(jìn)程有了初步的了解,本節(jié)通過一個(gè) Demo 來展示如何啟動(dòng)一批 Node.js 進(jìn)程來提供服務(wù)。

編寫主進(jìn)程

master.js 主要處理以下邏輯:

  • 創(chuàng)建一個(gè) server 并監(jiān)聽 3000 端口。
  • 根據(jù)系統(tǒng) cpus 開啟多個(gè)子進(jìn)程
  • 通過子進(jìn)程對(duì)象的 send 方法發(fā)送消息到子進(jìn)程進(jìn)行通信
  • 在主進(jìn)程中監(jiān)聽了子進(jìn)程的變化,如果是自殺信號(hào)重新啟動(dòng)一個(gè)工作進(jìn)程。
  • 主進(jìn)程在監(jiān)聽到退出消息的時(shí)候,先退出子進(jìn)程在退出主進(jìn)程
// 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'

const workers = {};
const createWorker = () => {
    const worker = fork('worker.js')
    worker.on('message', function (message) {
        if (message.act === 'suicide') {
            createWorker();
        }
    })
    worker.on('exit', function(code, signal) {
        console.log('worker process exited, code: %s signal: %s', code, signal);
        delete workers[worker.pid];
    });
    worker.send('server', server);
    workers[worker.pid] = worker;
    console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
}

for (let i=0; i<cpus.length; i++) {
    createWorker();
}

process.once('SIGINT', close.bind(this, 'SIGINT')); // kill(2) Ctrl-C
process.once('SIGQUIT', close.bind(this, 'SIGQUIT')); // kill(3) Ctrl-\
process.once('SIGTERM', close.bind(this, 'SIGTERM')); // kill(15) default
process.once('exit', close.bind(this));

function close (code) {
    console.log('進(jìn)程退出!', code);

    if (code !== 0) {
        for (let pid in workers) {
            console.log('master process exited, kill worker pid: ', pid);
            workers[pid].kill('SIGINT');
        }
    }

    process.exit(0);
}

工作進(jìn)程

worker.js 子進(jìn)程處理邏輯如下:

  • 創(chuàng)建一個(gè) server 對(duì)象,注意這里最開始并沒有監(jiān)聽 3000 端口
  • 通過 message 事件接收主進(jìn)程 send 方法發(fā)送的消息
  • 監(jiān)聽 uncaughtException 事件,捕獲未處理的異常,發(fā)送自殺信息由主進(jìn)程重建進(jìn)程,子進(jìn)程在鏈接關(guān)閉之后退出
// worker.js
const http = require('http');
const server = http.createServer((req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/plan'
    });
    res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid);
    throw new Error('worker process exception!'); // 測(cè)試異常進(jìn)程退出、重建
});

let worker;
process.title = 'node-worker'
process.on('message', function (message, sendHandle) {
    if (message === 'server') {
        worker = sendHandle;
        worker.on('connection', function(socket) {
            server.emit('connection', socket);
        });
    }
});

process.on('uncaughtException', function (err) {
    console.log(err);
    process.send({act: 'suicide'});
    worker.close(function () {
        process.exit(1);
    })
})

測(cè)試

控制臺(tái)執(zhí)行 node master.js 可以看到已成功創(chuàng)建了四個(gè)工作進(jìn)程

$ node master
worker process created, pid: 19280 ppid: 19279
worker process created, pid: 19281 ppid: 19279
worker process created, pid: 19282 ppid: 19279
worker process created, pid: 19283 ppid: 19279

打開活動(dòng)監(jiān)視器查看我們的進(jìn)程情況,由于在創(chuàng)建進(jìn)程時(shí)對(duì)進(jìn)程進(jìn)行了命名,很清楚的看到一個(gè)主進(jìn)程對(duì)應(yīng)多個(gè)子進(jìn)程。

圖片描述
以上 Demo 簡(jiǎn)單的介紹了多進(jìn)程創(chuàng)建、異常監(jiān)聽、重啟等,但是做為企業(yè)級(jí)應(yīng)用程序我們還需要考慮的更完善,例如:進(jìn)程的重啟次數(shù)限制、與守護(hù)進(jìn)程結(jié)合、多進(jìn)程模式下定時(shí)任務(wù)處理等,感興趣的同學(xué)推薦看下阿里 Egg.js 多進(jìn)程模式

守護(hù)進(jìn)程

關(guān)于守護(hù)進(jìn)程,是什么、為什么、怎么編寫?本節(jié)將解密這些疑點(diǎn)

守護(hù)進(jìn)程運(yùn)行在后臺(tái)不受終端的影響,什么意思呢?Node.js 開發(fā)的同學(xué)們可能熟悉,當(dāng)我們打開終端執(zhí)行 node app.js 開啟一個(gè)服務(wù)進(jìn)程之后,這個(gè)終端就會(huì)一直被占用,如果關(guān)掉終端,服務(wù)就會(huì)斷掉,即前臺(tái)運(yùn)行模式。如果采用守護(hù)進(jìn)程進(jìn)程方式,這個(gè)終端我執(zhí)行 node app.js 開啟一個(gè)服務(wù)進(jìn)程之后,我還可以在這個(gè)終端上做些別的事情,且不會(huì)相互影響。

創(chuàng)建步驟

  1. 創(chuàng)建子進(jìn)程
  2. 在子進(jìn)程中創(chuàng)建新會(huì)話(調(diào)用系統(tǒng)函數(shù) setsid)
  3. 改變子進(jìn)程工作目錄(如:“/” 或 “/usr/ 等)
  4. 父進(jìn)程終止

Node.js 編寫守護(hù)進(jìn)程 Demo 展示

index.js 文件里的處理邏輯使用 spawn 創(chuàng)建子進(jìn)程完成了上面的第一步操作。設(shè)置 options.detached 為 true 可以使子進(jìn)程在父進(jìn)程退出后繼續(xù)運(yùn)行(系統(tǒng)層會(huì)調(diào)用 setsid 方法),參考 options_detached,這是第二步操作。options.cwd 指定當(dāng)前子進(jìn)程工作目錄若不做設(shè)置默認(rèn)繼承當(dāng)前工作目錄,這是第三步操作。運(yùn)行 daemon.unref() 退出父進(jìn)程,參考 options.stdio,這是第四步操作。

// index.js
const spawn = require('child_process').spawn;

function startDaemon() {
    const daemon = spawn('node', ['daemon.js'], {
        cwd: '/usr',
        detached : true,
        stdio: 'ignore',
    });

    console.log('守護(hù)進(jìn)程開啟 父進(jìn)程 pid: %s, 守護(hù)進(jìn)程 pid: %s', process.pid, daemon.pid);
    daemon.unref();
}

startDaemon()

daemon.js 文件里處理邏輯開啟一個(gè)定時(shí)器每 10 秒執(zhí)行一次,使得這個(gè)資源不會(huì)退出,同時(shí)寫入日志到子進(jìn)程當(dāng)前工作目錄下

// /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);

守護(hù)進(jìn)程實(shí)現(xiàn) Node.js 版本 源碼地址

運(yùn)行測(cè)試

$ node index.js
守護(hù)進(jìn)程開啟 父進(jìn)程 pid: 47608, 守護(hù)進(jìn)程 pid: 47609

打開活動(dòng)監(jiān)視器查看,目前只有一個(gè)進(jìn)程 47609,這就是我們需要進(jìn)行守護(hù)的進(jìn)程

圖片描述

守護(hù)進(jìn)程閱讀推薦

守護(hù)進(jìn)程總結(jié)

在實(shí)際工作中對(duì)于守護(hù)進(jìn)程并不陌生,例如 PM2、Egg-Cluster 等,以上只是一個(gè)簡(jiǎn)單的 Demo 對(duì)守護(hù)進(jìn)程做了一個(gè)說明,在實(shí)際工作中對(duì)守護(hù)進(jìn)程的健壯性要求還是很高的,例如:進(jìn)程的異常監(jiān)聽、工作進(jìn)程管理調(diào)度、進(jìn)程掛掉之后重啟等等,這些還需要我們?nèi)ゲ粩嗨伎肌?/p>

關(guān)于本節(jié)內(nèi)容面試相關(guān)的推薦閱讀我的另一片篇文章 分享 10 道 Nodejs 進(jìn)程相關(guān)面試題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 很多Node.js初學(xué)者都會(huì)有這樣的疑惑,Node.js到底是單線程的還是多線程的?通過本章的學(xué)習(xí),能夠讓讀者較為...
    越努力越幸運(yùn)_952c閱讀 3,816評(píng)論 4 36
  • 前言 通過前邊的學(xué)習(xí),大家應(yīng)該已經(jīng)充分理解了node的單線程只不過是js層面的單線程,是基于V8引擎的單線程,因?yàn)?..
    白昔月閱讀 4,760評(píng)論 3 13
  • # 模塊機(jī)制 node采用模塊化結(jié)構(gòu),按照CommonJS規(guī)范定義和使用模塊,模塊與文件是一一對(duì)應(yīng)關(guān)系,即加載一個(gè)...
    RichRand閱讀 2,736評(píng)論 0 3
  • 文章翻譯自Node.js Child Processes: Everything you need to know...
    編程go閱讀 7,445評(píng)論 0 6
  • 希望閱讀本文,能讓你在學(xué)習(xí) child_process 和 cluster 模塊使用方法的同時(shí),對(duì) node 應(yīng)對(duì)...
    沐童Hankle閱讀 2,751評(píng)論 0 10

友情鏈接更多精彩內(nèi)容