亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

node怎么實現多進程和部署node項目

發布時間:2022-08-04 17:13:50 來源:億速云 閱讀:130 作者:iii 欄目:web開發

本篇內容介紹了“node怎么實現多進程和部署node項目”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

node怎么實現多進程和部署node項目

進程 VS 線程

進程

進程(process)是計算機操作系統分配和調度任務的基本單位。打開任務管理器,可以看到其實在計算機的后臺運行著非常多的程序,每個程序都是一個進程。

node怎么實現多進程和部署node項目

現代瀏覽器基本都是多進程架構的,以 Chrome 瀏覽器為例,打開“更多工具” - “任務管理器”,就能看到當前瀏覽器的進程信息,其中一個頁面就是一個進程,除此之外,還有網路進程,GPU 進程等。

node怎么實現多進程和部署node項目

多進程的架構,得以保證應用更穩定的運行。還是以瀏覽器為例,假如所有的程序都運行在一個進程中,如果網絡出現故障,或者頁面渲染出錯問題,都會導致整個瀏覽器的崩潰。通過多進程的架構,哪怕網絡進程崩潰了,它不會影響到已有頁面的展示,最壞也就是暫時不能接入網絡。

線程

線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。舉一個例子,一個程序好比是一家公司,下設多個部門,就是若干個進程;每個部門的通力合作使得公司正常運行,而線程就是員工,是具體干活的人。

我們都知道 JavaScript 是一門單線程語言。這么設計是因為早期 JS 主要用來編寫腳本程序,負責實現頁面的交互效果。如果設計成多線程語言,一是沒有必要,二是多個線程共同操作一個 dom 節點,那么瀏覽器該聽誰的?當然隨著技術的發展,現在的 JS 也支持了多線程,不過僅用來處理一些和 dom 操作無關的邏輯。

單進程存在的問題

單線程單進程帶來一個嚴重的問題,一個運行中的 node.js 程序,一旦主線程掛掉,那么這個進程也就掛掉了,整個應用也就隨之掛掉。再者,現代計算機大都是多核 CPU,四核八線程,八核十六線程,都是很常見的設備了。而 node.js 作為一個單進程的程序,白白浪費掉了多核 CPU 的性能。

針對這種情況,我們需要一個合適的多進程模型,將一個單進程的 node.js 程序變為多進程的架構。

Node.js 的多進程實現

Node.js 實現多進程架構有兩種常用方案,都是使用原生模塊,分別是 child_process 模塊和 cluster 模塊。

child_process

child_process 是 node.js 的內置模塊,看名字也能猜到它負責的是和子進程有關的事。

我們不再細說該模塊的具體用法,實際上它大概只有六七個方法,還是非常容易理解的。我們使用其中的一個 fork 方法來演示如何實現多進程以及多進程之間的通信。

先看下準備好的演示案例的目錄結構:

node怎么實現多進程和部署node項目

我們使用 http 模塊創建了一個 http server,當有 /sum 請求進來時,會通過 child_process 模塊創建一個子進程,并通知子進程執行計算的邏輯,同時父進程也要監聽子進程發來的消息:

// child_process.js

const http = require('http')
const { fork } = require('child_process')

const server = http.createServer((req, res) => {
  if (req.url == '/sum') {
    // fork 方法接收一個模塊路徑,然后開啟一個子進程,將模塊在子進程中運行
    // childProcess 表示創建的子進程
    let childProcess = fork('./sum.js')

    // 發消息給子進程
    childProcess.send('子進程開始計算')

    // 父進程中監聽子進程的消息
    childProcess.on('message', (data) => {
      res.end(data + '')
    })

    // 監聽子進程的關閉事件
    childProcess.on('close', () => {
      // 子進程正常退出和報錯掛掉,都會走到這里
      console.log('子進程關閉')
      childProcess.kill()
    })

    // 監聽子進程的錯誤事件
    childProcess.on('error', () => {
      console.log('子進程報錯')
      childProcess.kill()
    })
  }
    
  if (req.url == '/hello') {
    res.end('hello')
  }
  
  // 模擬父進程報錯
  if (req.url == '/error') {
     throw new Error('父進程出錯')
     res.end('hello')
   }
})
server.listen(3000, () => {
  console.log('Server is running on 3000')
})

sum.js 用來模擬子進程要執行的任務。子進程監聽父進程發來的消息,處理計算任務,然后將結果發送給父進程:

// sum.js

function getSum() {
  let sum = 0
  for (let i = 0; i < 10000 * 1000 * 100; i++) {
    sum += 1
  }

  return sum
}

// process 是 node.js 中一個全局對象,表示當前進程。在這里也就是子進程。
// 監聽主進程發來的消息
process.on('message', (data) => {
  console.log('主進程的消息:', data)
    
  const result = getSum()
  // 將計算結果發送給父進程
  process.send(result)
})

打開終端,運行命令 node 1.child_process

node怎么實現多進程和部署node項目

訪問瀏覽器:

node怎么實現多進程和部署node項目

接著來模擬子進程報錯的情況:

// sum.js

function getSum() {
  // ....
}

// 子進程運行5s后,模擬進程掛掉
 setTimeout(() => {
   throw new Error('報錯')
 }, 1000 * 5)

process.on('message', (data) => {
  // ...
})

再次訪問瀏覽器,5秒之后觀察控制臺:

node怎么實現多進程和部署node項目

子進程已經掛掉了,然后再訪問另一個 url :/hello

node怎么實現多進程和部署node項目

可見,父進程依然能正確處理請求,說明子進程報錯,并不會影響父進程的運行

接著我們來模擬父進程報錯的場景,注釋掉 sum.js 模塊的模擬報錯,然后重啟服務,瀏覽器訪問 /error

node怎么實現多進程和部署node項目

發現父進程掛掉后,整個 node.js 程序自動退出了,服務完全崩潰,沒有挽回的余地。

可見,通過 child_processfork 方法實現 node.js 的多進程架構并不復雜。進程間的通信主要通過 sendon 方法,從這個命名上也能知道,其底層應該是一個發布訂閱模式。

但是它存在一個嚴重的問題,雖然子進程不影響父進程,但是一旦父進程出錯掛掉,所有的子進程會被”一鍋端掉“ 。所以,這種方案適用于將一些復雜耗時的運算,fork 出一個單獨的子進程去做。更準確的來說,這種用法是用來代替多線程的實現,而非多進程。

cluster

使用 child_process 模塊實現多進程,貌似不堪大用。所以一般更推薦使用 cluster 模塊來實現 node.js 的多進程模型。

cluster,集群的意思,這個名詞相信大家都不陌生。打個比方,以前公司只有一個前臺,有時候太忙就沒辦法及時接待訪客。現在公司分配了4個前臺,即使有三個都在忙,也還有一個能接待新來的訪客。集群大致也就是這個意思,對于同一件事,合理的分配給不同的人去干,以此來保證這件事能做到最好。

cluster 模塊的使用也比較簡單。如果當前進程是主進程,則根據 CPU 的核數創建合適數量的子進程,同時監聽子進程的 exit 事件,有子進程退出,就重新 fork 新的子進程。如果不是子進程,則進行實際業務的處理。

const http = require('http')
const cluster = require('cluster')
const cpus = require('os').cpus()

if (cluster.isMaster) {
  // 程序啟動時首先走到這里,根據 CPU 的核數,創建出多個子進程
  for (let i = 0; i < cpus.length; i++) {
    // 創建出一個子進程
    cluster.fork()
  }

  // 當任何一個子進程掛掉后,cluster 模塊會發出'exit'事件。此時通過再次調用 fork 來來重啟進程。
  cluster.on('exit', () => {
    cluster.fork()
  })
} else {
  // fork 方法執行創建子進程,同時會再次執行該模塊,此時邏輯就會走到這里
  const server = http.createServer((req, res) => {
    console.log(process.pid)
    res.end('ok')
  })

  server.listen(3000, () => {
    console.log('Server is running on 3000', 'pid: ' + process.pid)
  })
}

啟動服務:

node怎么實現多進程和部署node項目

可以看到,cluster 模塊創建出了非常多的子進程,好像是每個子進程都運行著同一個web服務。

需要注意的是,此時并非是這些子進程共同監聽同一個端口。端口的監聽依然是由 createServer 方法創建的 server 去負責,將請求轉發給各個子進程。

我們編寫一個請求腳本,來請求上面的服務,看下效果。

// request.js

const http = require('http')

for (let i = 0; i < 1000; i++) {
  http.get('http://localhost:3000')
}

http 模塊不僅可以創建 http server,還能用來發送 http 請求。Axios支持瀏覽器和服務器環境,在服務器端就是使用 http 模塊發送 http 請求。

使用 node 命令執行該文件,再看下原來的控制臺:

node怎么實現多進程和部署node項目

打印出了具體處理請求的不同子進程的進程ID。

這就是通過 cluster 模塊實現的 nodd.js 的多進程架構。

當然,我們在部署 node.js 項目時不會這么干巴巴的寫和使用 cluster 模塊。有一個非常好用的工具,叫做 PM2,它是一個基于 cluster 模塊實現的進程管理工具。在后面的章節中會介紹它的基本用法。

小結

到此為止,我們花了一部分篇幅介紹 node.js 中多進程的知識,其實僅是想要交代下為什么需要使用 pm2 來管理 node.js 應用。本文由于篇幅有限,再加上描述不夠準確/詳盡,僅做簡單介紹。如果是第一次接觸這一塊內容的朋友,可能沒有太明白,也不打緊,后面會再出一篇更細節的文章。

部署實踐

準備一個 express 項目

本文已經準備了一個使用 express 開發的示例程序,點此訪問。

它主要實現了一個接口服務,當訪問 /api/users 時,使用 mockjs 模擬了10條用戶數據,返回一個用戶列表。同時會開啟一個定時器,來模擬報錯的情況:

const express = require('express')
const Mock = require('mockjs')

const app = express()

app.get("/api/users", (req, res) => {
  const userList = Mock.mock({
    'userList|10': [{
      'id|+1': 1,
      'name': '@cname',
      'email': '@email'
    }]
  })
  
  setTimeout(()=> {
      throw new Error('服務器故障')
  }, 5000)

  res.status(200)
  res.json(userList)
})

app.listen(3000, () => {
  console.log("服務啟動: 3000")
})

本地測試一下,在終端中執行命令:

node server.js

打開瀏覽器,訪問用戶列表接口:

node怎么實現多進程和部署node項目

五秒鐘后,服務器會掛掉:

node怎么實現多進程和部署node項目

后面我們使用 pm2 來管理應用后,就可以解決這個問題。

討論:express 項目是否需要打包

通常完成一個 vue/react 項目后,我們都會先執行打包,再進行發布。其實前端項目要進行打包,主要是因為程序最終的運行環境是瀏覽器,而瀏覽器存在各種兼容性問題和性能問題,比如:

  • 高級語法的不支持,需要將 ES6+ 編譯為 ES5 語法

  • 不能識別 .vue.jsx.ts 文件,需要編譯

  • 減少代碼體積,節省帶寬資源,提高資源加載速度

  • ......

而使用 express.js 或者 koa.js 開發的項目,并不存在這些問題。并且,Node.js 采用 CommonJS 模塊化規范,有緩存的機制;同時,只有當模塊在被用到時,才會被導入。如果進行打包,打包成一個文件,其實就浪費了這個優勢。所以針對 node.js 項目,并不需要打包。

服務器安裝 Node.js

本文以 CentOS 系統為例進行演示。

NVM

為了方便切換 node 的版本,我們使用 nvm 來管理 node。

Nvm(Node Version Manager) ,就是 Node.js 的版本管理工具。通過它,可以讓 node 在多個版本之間進行任意切換,避免了需要切換版本時反復的下載和安裝的操作。

Nvm的官方倉庫是 github.com/nvm-sh/nvm。因為它的安裝腳本存放在 githubusercontent 站點上,經常訪問不了。所以我在 gitee 上新建了它的鏡像倉庫,這樣就能從 gitee 上訪問到它的安裝腳本了。

通過 curl 命令下載安裝腳本,并使用 bash 執行腳本,會自動完成 nvm 的安裝工作:

# curl -o- https://gitee.com/hsyq/nvm/raw/master/install.sh | bash

當安裝完成之后,我們再打開一個新的窗口,來使用 nvm :

[root@ecs-221238 ~]# nvm 
-v0.39.1

可以正常打印版本號,說明 nvm 已經安裝成功了。

安裝 Node.js

現在就可以使用 nvm 來安裝和管理 node 了。

查看可用的 node 版本:

# nvm ls-remote

安裝 node:

# nvm install 18.0.0

查看已經安裝的 node 版本:

[root@ecs-221238 ~]# nvm list
->      v18.0.0
default -> 18.0.0 (-> v18.0.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v18.0.0) (default)
stable -> 18.0 (-> v18.0.0) (default)

選擇一個版本進行使用:

# nvm use 18.0.0

需要注意的一點,在 Windows 上使用 nvm 時,需要使用管理員權限執行 nvm 命令。在 CentOS 上,我默認使用 root 用戶登錄的,因而沒有出現問題。大家在使用時遇到了未知錯誤,可以搜索一下解決方案,或者嘗試下是否是權限導致的問題。

在安裝 node 的時候,會自動安裝 npm。查看 node 和 npm 的版本號:

[root@ecs-221238 ~]# node -v
v18.0.0

[root@ecs-221238 ~]# npm -v
8.6.0

默認的 npm 鏡像源是官方地址:

[root@ecs-221238 ~]# npm config get registry
https://registry.npmjs.org/

切換為國內淘寶的鏡像源:

[root@ecs-221238 ~]# npm config set registry https://registry.npmmirror.com

到此為止,服務器就已經安裝好 node 環境和配置好 npm 了。

項目上傳到服務器

方法有很多,或者從 Github / GitLab / Gitee 倉庫中下載到服務器中,或者本地通過 ftp 工具上傳。步驟很簡單,不再演示。

演示項目放到了 /www 目錄 下:

node怎么實現多進程和部署node項目

服務器開放端口

一般云服務器僅開放了 22 端口用于遠程登錄。而常用的80,443等端口并未開放。另外,我們準備好的 express 項目運行在3000端口上。所以需要先到云服務器的控制臺中,找到安全組,添加幾條規則,開放80和3000端口。

node怎么實現多進程和部署node項目

node怎么實現多進程和部署node項目

使用 PM2 管理應用

在開發階段,我們可以使用 nodemon 來做實時監聽和自動重啟,提高開發效率。在生產環境,就需要祭出大殺器—PM2了。

基本使用

首先全局安裝 pm2:

# npm i -g pm2

執行 pm2 -v 命令查看是否安裝成功:

[root@ecs-221238 ~]# pm2 
-v5.2.0

切換到項目目錄,先把依賴裝上:

cd /www/express-demo
npm install

然后使用 pm2 命令來啟動應用。

pm2 start app.js -i max
// 或者
pm2 start server.js -i 2

PM2 管理應用有 fork 和 cluster 兩種模式。在啟動應用時,通過使用 -i 參數來指定實例的個數,會自動開啟 cluster 模式。此時就具備了負載均衡的能力。

-i :instance,實例的個數。可以寫具體的數字,也可以配置成 max,PM2會自動檢查可用的CPU的數量,然后盡可能多地啟動進程。

node怎么實現多進程和部署node項目

此時應用就啟動好了。PM2 會以守護進程的形式管理應用,這個表格展示了應用運行的一些信息,比如運行狀態,CPU使用率,內存使用率等。

在本地的瀏覽器中訪問接口:

node怎么實現多進程和部署node項目

Cluster 模式是一個多進程多實例的模型,請求進來后會分配給其中一個進程處理。正如前面我們看過的 cluster 模塊的用法一樣,由于 pm2 的守護,即使某個進程掛掉了,也會立刻重啟該進程。

回到服務器終端,執行 pm2 logs 命令,查看下 pm2 的日志:

node怎么實現多進程和部署node項目

可見,id 為1的應用實例掛掉了,pm2 會立刻重啟該實例。注意,這里的 id 是應用實例的 id,并非進程 id。

到這里,一個 express 項目的簡單部署就完成了。通過使用 pm2 工具,基本能保證我們的項目可以穩定可靠的運行。

PM2 常用命令小結

這里整理了一些 pm2 工具常用的命令,可供查詢參考。

# Fork模式
pm2 start app.js --name app # 設定應用的名字為 app

# Cluster模式
# 使用負載均衡啟動4個進程
pm2 start app.js -i 4     

# 將使用負載均衡啟動4個進程,具體取決于可用的 CPU
pm2 start app.js -i 0   

# 等同于上面命令的作用
pm2 start app.js -i max 

 # 給 app 擴展額外的3個進程
pm2 scale app +3

# 將 app 擴展或者收縮到2個進程
pm2 scale app 2              

# 查看應用狀態
# 展示所有進程的狀態
pm2 list  

# 用原始 JSON 格式打印所有進程列表
pm2 jlist

# 用美化的 JSON 打印所有進程列表
pm2 prettylist  

# 展示特定進程的所有信息
pm2 describe 0

# 使用儀表盤監控所有進程
pm2 monit             

# 日志管理

# 實時展示所有應用的日志
pm2 logs          

# 實時展示 app 應用的日志 
pm2 logs app

# 使用json格式實時展示日志,不輸出舊日志,只輸出新產生的日志
pm2 logs --json

# 應用管理

# 停止所有進程
pm2 stop all

# 重啟所有進程
pm2 restart all       
# 停止指定id的進程
pm2 stop 0     

# 重啟指定id的進程
pm2 restart 0         

# 刪除id為0進程
pm2 delete 0

# 刪除所有的進程
pm2 delete all

每一條命令都可以親自嘗試一下,看看效果。

這里特別展示下 monit 命令,它可以在終端中啟動一個面板,實時展示應用的運行狀態,通過上下箭頭可以切換 pm2 管理的所有應用:

node怎么實現多進程和部署node項目

進階:使用 pm2 配置文件

PM2 的功能十分強大,遠不止上面的這幾個命令。在真實的項目部署中,可能還需要配置日志文件,watch 模式,環境變量等等。如果每次都手敲命令是十分繁瑣的,所以 pm2 提供了配置文件來管理和部署應用。

可以通過以下命令來生成一份配置文件:

[root@ecs-221238 express-demo]# pm2 init simple
File /www/express-demo/ecosystem.config.js generated

會生成一個ecosystem.config.js 文件:

module.exports = {
  apps : [{
    name   : "app1",
    script : "./app.js"
  }]
}

也可以自己創建一個配置文件,比如 app.config.js

const path = require('path')

module.exports = {
  // 一份配置文件可以同時管理多個 node.js 應用
  // apps 是一個數組,每一項都是一個應用的配置
  apps: [{
    // 應用名稱
    name: "express-demo",

    // 應用入口文件
    script: "./server.js",

    // 啟動應用的模式, 有兩種:cluster和fork,默認是fork
    exec_mode: 'cluster',

    // 創建應用實例的數量
    instances: 'max',

    // 開啟監聽,當文件變化后自動重啟應用
    watch: true,

    // 忽略掉一些目錄文件的變化。
    // 由于把日志目錄放到了項目路徑下,一定要將其忽略,否則應用啟動產生日志,pm2 監聽到變化就會重啟,重啟又產生日志,就會進入死循環
    ignore_watch: [
      "node_modules",
      "logs"
    ],
    // 錯誤日志存放路徑
    err_file: path.resolve(__dirname, 'logs/error.log'),

    // 打印日志存放路徑
    out_file: path.resolve(__dirname, 'logs/out.log'),

    // 設置日志文件中每條日志前面的日期格式
    log_date_format: "YYYY-MM-DD HH:mm:ss",
  }]
}

讓 pm2 使用配置文件來管理 node 應用:

pm2 start app.config.js

現在 pm2 管理的應用,會將日志放到項目目錄下(默認是放到 pm2 的安裝目錄下),并且能監聽文件的變化,自動重啟服務。

node怎么實現多進程和部署node項目

更多有用的配置可以參考 PM2 官方文檔,點此訪問。

Nginx 代理轉發接口

上面我們直接將 nodejs 項目的3000端口暴露了出去。一般我們都會使用 nginx 做一個代理轉發,只對外暴露 80 端口。

安裝 Nginx

首先服務器中需要安裝 nginx ,有三種方式:

  • 下載源碼編譯安裝

  • 使用 docker 安裝

  • 使用包管理工具安裝

我這里的系統是 CentOS 8,已經更換了可用的 yum 源,可以直接安裝 nginx。如果你的操作系統為 CentOS 7 或者其他發行版,可以搜索適合的安裝方法。

使用 yum 安裝:

# yum install -y nginx

然后啟動 nginx:

# systemctl start nginx

打開瀏覽器訪問服務器地址,可以看到 nginx 默認的主頁:

node怎么實現多進程和部署node項目

配置接口轉發

為項目新建一個配置文件:

# vim /etc/nginx/conf.d/express.conf

監聽80端口,將所有請求轉發給服務器本地的3000端口的程序處理:

server {
    listen       80;
    server_name  ironfan.site;
    location / {
          proxy_pass http://localhost:3000;
    }
}

conf 目錄下的配置文件,會被主配置文件 /etc/nginx/nginx.conf 加載:

node怎么實現多進程和部署node項目

修改完配置文件,一定要重啟服務:

# systemctl restart nginx

然后本地打開瀏覽器,去掉原來的3000端口號,直接訪問完整的 url:

node怎么實現多進程和部署node項目

到這里,就完成了接口轉發的配置。從用戶的角度出發,這個也叫反向代理。

undefined

“node怎么實現多進程和部署node項目”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阿克陶县| 卢湾区| 汉寿县| 老河口市| 高州市| 安康市| 松原市| 翁牛特旗| 河南省| 伊金霍洛旗| 柳江县| 改则县| 道孚县| 中江县| 吉安县| 延边| 讷河市| 荣昌县| 中西区| 南靖县| 恩平市| 大英县| 精河县| 凤山市| 西宁市| 磐石市| 河曲县| 巴林左旗| 耿马| 荃湾区| 防城港市| 上林县| 邵阳县| 镇平县| 嘉荫县| 南皮县| 吉木乃县| 腾冲县| 潜山县| 通海县| 鱼台县|