<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 前置知識 > JavaScript 代碼在計算機進程中的單個線程上執行。它的代碼在這個線程上進行同步處理,每次只運行一條指令。因此,如果我們要在這個線程上執行一個長時間運行的任務,那么后續的代碼都將被阻塞,直到任務完成。 Node.js 遵循的是單線程單進程的模式,node 的單線程是指 js 的引擎只有一個實例,且在 nodejs 的主線程中執行,同時 node 以事件驅動的方式處理 IO 等異步操作。 node 的單線程模式,只維持一個主線程,大大減少了線程間切換的開銷。 它的優勢是沒有線程間數據同步的性能消耗也不會出現死鎖的情況。所以它是線程安全并且性能高效的。 單線程有它的弱點,以單一進程運行,無法充分利用多核 CPU 資源,**CPU 密集型計算**(即只用 CPU 計算的操作,比如要對數據加解密(node.bcrypt.js),數據壓縮和解壓 (node-tar))可能會導致 I/O 阻塞,以及出現錯誤可能會導致應用崩潰。 # 解決單線程弱點 ## 瀏覽器端 HTML5 制定了 Web Worker 標準(Web Worker 的作用,就是為 JavaScript 創造多線程環境,允許主線程創建 Worker 線程,將一些任務分配給后者運行)。 > http://www.ruanyifeng.com/blog/2018/07/web-worker.html ## Node 端 采用了和 Web Worker 相同的思路來解決單線程中大量計算問題 ,官方提供了 `child_process` 模塊和 `cluster` 模塊。 1. `cluster` 模塊:為了調度多核 CPU 等資源,利用多核 CPU 的資源,使得可以通過一串 node 子進程去處理負載任務,同時保證一定的負載均衡型。 2. `child_process` 模塊:為了進行 CPU 密集型操作,不阻塞主線程。**創建獨立的子進程**,父子進程通過 **IPC 通信**,子進程可以是外部應用也可以是 node 子程序,子進程執行后可以將結果返回給父進程。 `child_process`、`cluster` (底層是基于 `child_process` 實現),都是用于創建子進程,然后子進程間通過事件消息來傳遞結果,這個可以很好地保持應用模型的簡單和低依賴。 ## `child_process` 模塊 `child_process`的實例,表示一個系統子進程,并執行 shell 命令,在與系統層面的交互上挺有用處。 NodeJS 子進程提供了與系統交互的重要接口,其主要 API 有: * 標準輸入、標準輸出及標準錯誤輸出的接口 * `child.stdin`?獲取標準輸入 * `child.stdout`?獲取標準輸出 * `child.stderr`?獲取標準錯誤輸出 * 獲取子進程的PID:`child.pid` * 提供生成子進程的重要方法:`child_process.spawn(cmd, args=[], [options])` * 提供直接執行系統命令的重要方法:`child_process.exec(cmd, [options], callback)` * 提供殺死進程的方法:`child.kill(signal='SIGTERM')` 下面都是默認異步創建子進程的方式,,子進程的運行不會阻塞主進程,每一種方式都有對應的同步版本(`execFileSync`、`spawnSync` 和 `execSync`)。 * `.exec()`、`.execFile()`、`.fork()`底層都是通過`.spawn()`實現的。 * `.exec()`、`execFile()`額外提供了回調,當子進程停止的時候執行。 ``` child_process.spawn(command[, args][, options]) child_process.exec(command[, options][, callback]) child_process.execFile(file[, args][, options][, callback]) child_process.fork(modulePath[, args][, options]) ``` `child_process.spawn`利用命令行創建一個子進程,并且可以控制子進程的啟動,終止,以及通信: ```javascript /*************** * spawn 創建了一個子進程,并返回一個進程描述符,即句柄 * 進程句柄都有一個 stdout 屬性,以流的形式輸出進程的標準輸出信息 * 可以在這個輸出流上綁定事件,監視每個輸出 * ****************/ // tail 命令會監控一個文件(不存在則退出), // 如果文件發生改變則在標準輸出流中輸出文件內容 let spawn = require('child_process').spawn; // 創建一個子進程,將進程描述符賦值給child let child = spawn('tail', ['-f', './test']); // 監聽標準輸出流 child.stdout.on('data', function (data) { console.log('tail output: ' + data); }); // 終止進程 setTimeout(() => { // 默認發送 SIGTERM child.kill(); }, 1000); // 監聽子進程退出事件 child.on('exit', (code, signal) => { if (code) { // 正常退出會有一個退出碼,0為正常退出,非0一般表示錯誤 console.log('child process terminated with code ' + code); } else { // 非正常退出,輸出退出信號 console.log('child process terminated with signal ' + signal); } }); ``` ??示例中 `child.on('exit', (code, signal) => {}) `: 參數:`code`、`signal`,如果子進程是自己退出的,那么`code`就是退出碼,否則為`null`;如果子進程是通過信號結束的,那么,`signal`就是結束進程的信號,否則為`null`。這兩者中,肯定有一個不為`null`。 注意事項:`exit`事件觸發時,子進程的 stdio stream 可能還打開著。(場景?)此外,node 監聽了`SIGINT`和`SIGTERM`信號,也就是說,node 收到這兩個信號時,不會立刻退出,而是先做一些清理的工作,然后重新拋出這兩個信號。(目測此時js可以做清理工作了,比如關閉數據庫等。) * SIGINT:`interrupt`,程序終止信號,通常在用戶按下`CTRL+C`時發出,用來通知前臺進程終止進程。 * SIGTERM:`terminate`,程序結束信號,該信號可以被阻塞和處理,通常用來要求程序自己正常退出。shell 命令`kill`默認產生這個信號。如果信號終止不了,我們才會嘗試`SIGKILL`(強制終止)。 > [child_process](https://www.jianshu.com/p/03bbc306088e) ## `cluster` 模塊 根據多核 CPU 創建子進程后,自動控制負載均衡的方式。 我們將 master 稱為主進程,而 worker 進程稱為工作進程,利用 `cluster` 模塊,使用 node 封裝好的 API、**IPC 通道**和調度機可以非常簡單的創建包括一個 master 進程下 HTTP 代理服務器 + 多個 worker 進程多個 HTTP 應用服務器的架構。 從官網的例子來看: ``` const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`主進程 ${process.pid} 正在運行`); // 衍生工作進程。 for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`工作進程 ${worker.process.pid} 已退出`); }); } else { // 工作進程可以共享任何 TCP 連接。 // 在本例子中,共享的是一個 HTTP 服務器。 http.createServer((req, res) => { res.writeHead(200); res.end('你好世界\n'); }).listen(8000); console.log(`工作進程 ${process.pid} 已啟動`); } ``` 最后輸出的結果為: ``` $ node server.js 主進程 3596 正在運行 工作進程 4324 已啟動 工作進程 4520 已啟動 工作進程 6056 已啟動 工作進程 5644 已啟動 ``` ### 重要代表 pm2 pm2優點很多: * 負載均衡 * 熱重載:0s reload * 非常好的測試覆蓋率 pm2 啟動很簡單: ``` $ pm2 start server.js -i 4 -l ./log.txt ``` * `-i 4` 是 cpu數量(我是4核的) * `-l ./log.txt` 打日志 pm2 啟動后自動到后臺執行 通過?`$ pm2 list`?可以查看正在跑著那些進程。 更多內容直接看官網:?http://pm2.keymetrics.io/ # 多線程 Node V10.5.0: 提供了實驗性質的 `worker_threads` 模塊,才讓 Node 擁有了多工作線程。 Node V12.0.0:`worker_threads` 已經成為正式標準,可以在生產環境放心使用。 也有很多開發者認為 `worker_threads` 違背了 nodejs 設計的初衷,事實上那是它并沒有真正理解 `worker_threads` 的底層原理。 ## `worker_threads` 模塊 `worker_threads` (工作線程)對于執行 CPU 密集型的 JavaScript 操作非常有用。它們對 I/O 密集型工作沒有多大幫助。js 的內置異步 I/O 操作比 Workers 效率更高。 `worker_threads` 比使用 `child_process` 或 cluster 可以獲得的并行性更輕量級。 此外,`worker_threads` 可以有效地共享內存。通過傳輸 ArrayBuffer 實例或共享 SharedArrayBuffer 實例來實現。 1. 加載 `worker_threads` 模塊 node.js v10.5.0 引入的實驗性質 API,開啟時需要使用 `--experimental-worker` 參數。 node.js v12.0.0 里面默認開啟,也預示著您可以將該特性用于生產環境中。 2. 示例 ``` const { Worker, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { // This code is executed in the main thread and not in the worker. // Create the worker. const worker = new Worker(__filename); // Listen for messages from the worker and print them. worker.on('message', (msg) => { console.log(msg); }); } else { // This code is executed in the worker and not in the main thread. // Send a message to the main thread. parentPort.postMessage('Hello world!'); } ``` * Worker: 該類用于創建 worker 對象。有一個必填參數`__filename`(文件路徑),該文件會被 worker 執行。同時我們可以在主線程中通過 `worker.on` 監聽 `message` 事件 * isMainThread: 該對象用于區分是主線程(true)還是工作線程(false) * parentPort: 該對象的 `postMessage` 方法用于 worker 線程向主線程發送消息 # 進程通信方式 ## 通過`stdin/stdout`等傳遞 ## 原生 IPC 方式 ## 通過網絡 Sockets # 參考 [Nodejs 學習筆記以及經驗總結/cluster.md](https://github.com/chyingp/nodejs-learning-guide/blob/master/%E6%A8%A1%E5%9D%97/cluster.md) [nodejs-learning-guide](https://github.com/chyingp/nodejs-learning-guide/blob/master/%E6%A8%A1%E5%9D%97/child_process.md)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看