日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用 Node.js Express 的最佳实践

發(fā)布時間:2023/12/19 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用 Node.js Express 的最佳实践 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Production best practices: performance and reliability

本文討論部署到生產(chǎn)的 Express 應用程序的性能和可靠性最佳實踐。

這個話題顯然屬于“devops”世界,涵蓋傳統(tǒng)的開發(fā)和運營。 因此,信息分為兩部分:

在您的代碼中要做的事情(開發(fā)部分)

  • 使用 gzip 壓縮
  • 不要使用同步函數(shù)
  • 正確記錄
  • 正確處理異常

在您的環(huán)境/設置中要做的事情(操作部分)

  • 將 NODE_ENV 設置為“生產(chǎn)”
  • 確保您的應用自動重啟
  • 在集群中運行您的應用程序
  • 緩存請求結果
  • 使用負載均衡器
  • 使用反向代理

Use gzip compression

Gzip 壓縮可以大大減小響應主體的大小,從而提高 Web 應用程序的速度。 在您的 Express 應用程序中使用 compression 進行 gzip 壓縮。 例如:

var compression = require('compression') var express = require('express') var app = express() app.use(compression())

對于生產(chǎn)中的高流量網(wǎng)站,實施壓縮的最佳方法是在反向代理級別實施它。 在這種情況下,您不需要使用 compression 中間件。 有關在 Nginx 中啟用 gzip 壓縮的詳細信息,請參閱 Nginx 文檔中的模塊 ngx_http_gzip_module。

Don’t use synchronous functions

同步函數(shù)和方法會在它們返回之前阻塞正在執(zhí)行的進程。 對同步函數(shù)的單個調用可能會在幾微秒或幾毫秒內返回,但是在高流量網(wǎng)站中,這些調用會累加并降低應用程序的性能。 避免在生產(chǎn)中使用它們。

盡管 Node 和許多模塊提供了它們功能的同步和異步版本,但在生產(chǎn)中始終使用異步版本。 唯一可以證明同步功能合理的時間是在初始啟動時。

如果您使用的是 Node.js 4.0+ 或 io.js 2.1.0+,您可以使用 --trace-sync-io 命令行標志在您的應用程序使用同步 API 時打印警告和堆棧跟蹤。 當然,您不想在生產(chǎn)中使用它,而是要確保您的代碼已準備好用于生產(chǎn)。

Do logging correctly

通常,從您的應用程序進行日志記錄有兩個原因:用于調試和記錄應用程序活動(本質上是其他所有內容)。 使用 console.log() 或 console.error() 將日志消息打印到終端是開發(fā)中的常見做法。 但是當目標是終端或文件時,這些函數(shù)是同步的,因此它們不適合生產(chǎn),除非您將輸出通過管道傳輸?shù)搅硪粋€程序。

如果您出于調試目的進行日志記錄,那么不要使用 console.log(),而是使用像 debug 這樣的特殊調試模塊。 此模塊使您能夠使用 DEBUG 環(huán)境變量來控制將哪些調試消息發(fā)送到 console.error()(如果有)。 為了保持你的應用完全異步,你仍然希望通過管道將 console.error() 傳遞給另一個程序。

如果您要記錄應用活動(例如,跟蹤流量或 API 調用),請不要使用 console.log(),而是使用像 Winston 或 Bunyan 這樣的日志庫。 有關這兩個庫的詳細比較,請參閱 StrongLoop 博客文章 Comparing Winston and Bunyan Node.js Logging.

Handle exceptions properly

Node 應用程序在遇到未捕獲的異常時崩潰。 不處理異常并采取適當?shù)拇胧⑹鼓?Express 應用程序崩潰并下線。 如果您遵循下面確保您的應用程序自動重新啟動中的建議,那么您的應用程序將從崩潰中恢復。 幸運的是,Express 應用程序的啟動時間通常很短。 盡管如此,您首先要避免崩潰,為此,您需要正確處理異常。

為確保您處理所有異常,請使用以下技術:

  • try-catch
  • promises

在深入研究這些主題之前,您應該對 Node/Express 錯誤處理有一個基本的了解:使用錯誤優(yōu)先回調,以及在中間件中傳播錯誤。 Node 使用“錯誤優(yōu)先回調”約定從異步函數(shù)返回錯誤,其中回調函數(shù)的第一個參數(shù)是錯誤對象,后跟參數(shù)中的結果數(shù)據(jù)。 要指示沒有錯誤,請將 null 作為第一個參數(shù)傳遞。 回調函數(shù)必須相應地遵循錯誤優(yōu)先回調約定才能有意義地處理錯誤。 而在 Express 中,最佳實踐是使用 next() 函數(shù)通過中間件鏈傳播錯誤。

What not to do

您不應該做的一件事是偵聽 uncaughtException 事件,當異常冒泡一直返回到事件循環(huán)時會發(fā)出該事件。 為 uncaughtException 添加事件監(jiān)聽器將改變遇到異常的進程的默認行為; 盡管有異常,該進程仍將繼續(xù)運行。 這聽起來像是防止您的應用程序崩潰的好方法,但在未捕獲的異常之后繼續(xù)運行應用程序是一種危險的做法,不建議這樣做,因為進程的狀態(tài)變得不可靠且不可預測。

此外,使用 uncaughtException 被官方認為是粗暴的。所以監(jiān)聽 uncaughtException 只是一個壞主意。 這就是為什么我們推薦多個進程和主管之類的東西:崩潰和重新啟動通常是從錯誤中恢復的最可靠方法。

我們也不建議使用 domains.它通常不能解決問題,并且是不推薦使用的模塊。

Use try-catch

Try-catch 是一種 JavaScript 語言結構,可用于捕獲同步代碼中的異常。 例如,使用 try-catch 來處理 JSON 解析錯誤,如下所示。

使用諸如 JSHint 或 JSLint 之類的工具來幫助您查找隱式異常,例如未定義變量上的引用錯誤。

以下是使用 try-catch 處理潛在進程崩潰異常的示例。 這個中間件函數(shù)接受一個名為“params”的查詢字段參數(shù),它是一個 JSON 對象。

app.get('/search', (req, res) => {// Simulating async operationsetImmediate(() => {var jsonStr = req.query.paramstry {var jsonObj = JSON.parse(jsonStr)res.send('Success')} catch (e) {res.status(400).send('Invalid JSON string')}}) })

但是,try-catch 僅適用于同步代碼。 因為 Node 平臺主要是異步的(特別是在生產(chǎn)環(huán)境中),try-catch 不會捕獲很多異常。

Use promises

Promise 將處理使用 then() 的異步代碼塊中的任何異常(顯式和隱式)。 只需將 .catch(next) 添加到承諾鏈的末尾即可。 例如:

app.get('/', (req, res, next) => {// do some sync stuffqueryDb().then((data) => makeCsv(data)) // handle data.then((csv) => { /* handle csv */ }).catch(next) })app.use((err, req, res, next) => {// handle error })

現(xiàn)在所有異步和同步錯誤都會傳播到錯誤中間件。

但是,有兩個警告:

  • 所有異步代碼都必須返回承諾(發(fā)射器除外)。 如果特定庫不返回承諾,請使用 Bluebird.promisifyAll() 等輔助函數(shù)轉換基礎對象。

  • 事件發(fā)射器(如流)仍然會導致未捕獲的異常。 因此,請確保正確處理錯誤事件; 例如:

const wrap = fn => (...args) => fn(...args).catch(args[2])app.get('/', wrap(async (req, res, next) => {const company = await getCompanyById(req.query.id)const stream = getLogoStreamById(company.id)stream.on('error', next).pipe(res) }))

wrap() 函數(shù)是一個包裝器,它捕獲被拒絕的承諾并調用 next() 并將錯誤作為第一個參數(shù)。

更多細節(jié)可以參考這篇博客:Asynchronous Error Handling in Express with Promises, Generators and ES7.

Set NODE_ENV to “production”

NODE_ENV 環(huán)境變量指定應用程序運行的環(huán)境(通常是開發(fā)環(huán)境或生產(chǎn)環(huán)境)。 為了提高性能,您可以做的最簡單的事情之一是將 NODE_ENV 設置為“production”。

將 NODE_ENV 設置為“production”使得 Express:

  • 緩存視圖模板。
  • 緩存從 CSS 擴展生成的 CSS 文件。
  • 生成不太詳細的錯誤消息。

如果您需要編寫特定于環(huán)境的代碼,您可以使用 process.env.NODE_ENV 檢查 NODE_ENV 的值。 請注意,檢查任何環(huán)境變量的值都會導致性能下降,因此應謹慎進行。

在開發(fā)中,您通常在交互式 shell 中設置環(huán)境變量,例如使用 export 或 .bash_profile 文件。 但一般來說,你不應該在生產(chǎn)服務器上這樣做; 相反,請使用您操作系統(tǒng)的初始化系統(tǒng)(systemd 或 Upstart)。 下一節(jié)提供了有關使用 init 系統(tǒng)的更多詳細信息,但設置 NODE_ENV 對性能非常重要(并且易于操作),因此在此處突出顯示。

使用 Upstart,在您的作業(yè)文件中使用 env 關鍵字。 例如:

# /etc/init/env.confenv NODE_ENV=production

使用 systemd,在單元文件中使用 Environment 指令。 例如:

# /etc/systemd/system/myservice.service Environment=NODE_ENV=production

Ensure your app automatically restarts

在生產(chǎn)中,您永遠不希望您的應用程序處于離線狀態(tài)。 這意味著您需要確保它在應用程序崩潰和服務器本身崩潰時重新啟動。 盡管您希望這兩種情況都不會發(fā)生,但實際上您必須通過以下方式對這兩種情況進行說明:

  • 使用進程管理器在崩潰時重新啟動應用程序(和節(jié)點)。

  • 使用操作系統(tǒng)提供的 init 系統(tǒng)在操作系統(tǒng)崩潰時重新啟動進程管理器。 也可以在沒有進程管理器的情況下使用 init 系統(tǒng)。

如果遇到未捕獲的異常,節(jié)點應用程序就會崩潰。 您需要做的最重要的事情是確保您的應用程序經(jīng)過良好測試并處理所有異常。

但作為一種故障安全措施,應采用一種機制來確保當您的應用程序崩潰時,它會自動重新啟動。

Use a process manager

在開發(fā)中,您只需使用 node server.js 或類似的東西從命令行啟動您的應用程序。 但是在生產(chǎn)中這樣做會導致災難。 如果應用程序崩潰,它將處于離線狀態(tài),直到您重新啟動它。 為確保您的應用程序在崩潰時重新啟動,請使用進程管理器。 流程管理器是應用程序的“容器”,可促進部署、提供高可用性并使您能夠在運行時管理應用程序。

除了在應用程序崩潰時重新啟動應用程序之外,進程管理器還可以讓您:

  • 深入了解運行時性能和資源消耗。

  • 動態(tài)修改設置以提高性能。

  • 控制集群(StrongLoop PM 和 pm2)。

下面是三個比較流行的進程管理器:

  • StrongLoop Process Manager
  • PM2
  • Forever

有關三個流程管理器的逐個功能比較,請參閱 http://strong-pm.io/compare/。

使用這些進程管理器中的任何一個都足以讓您的應用程序保持正常運行,即使它不時崩潰。

Use an init system

下一層可靠性是確保您的應用程序在服務器重新啟動時重新啟動。 由于各種原因,系統(tǒng)仍可能出現(xiàn)故障。 為確保您的應用程序在服務器崩潰時重新啟動,請使用操作系統(tǒng)內置的 init 系統(tǒng)。 目前使用的兩個主要初始化系統(tǒng)是 systemd 和 Upstart。

有兩種方法可以在 Express 應用程序中使用 init 系統(tǒng):

  • 在進程管理器中運行您的應用程序,并使用 init 系統(tǒng)將進程管理器安裝為服務。 當應用程序崩潰時,進程管理器將重新啟動您的應用程序,當操作系統(tǒng)重新啟動時,init 系統(tǒng)將重新啟動進程管理器。 這是推薦的方法。

  • 直接使用 init 系統(tǒng)運行您的應用程序(和 Node)。 這有點簡單,但您無法獲得使用進程管理器的額外優(yōu)勢。

Systemd

Systemd 是一個 Linux 系統(tǒng)和服務管理器。 大多數(shù)主要的 Linux 發(fā)行版都采用 systemd 作為它們的默認初始化系統(tǒng)。

systemd 服務配置文件稱為單元文件,文件名以 .service 結尾。 這是一個用于直接管理 Node 應用程序的示例單元文件。 為您的系統(tǒng)和應用替換尖括號中的值:

[Unit] Description=<Awesome Express App>[Service] Type=simple ExecStart=/usr/local/bin/node </projects/myapp/index.js> WorkingDirectory=</projects/myapp>User=nobody Group=nogroup# Environment variables: Environment=NODE_ENV=production# Allow many incoming connections LimitNOFILE=infinity# Allow core dumps for debugging LimitCORE=infinityStandardInput=null StandardOutput=syslog StandardError=syslog Restart=always[Install] WantedBy=multi-user.target

Run your app in a cluster

在多核系統(tǒng)中,您可以通過啟動一組進程來將 Node 應用程序的性能提高許多倍。 一個集群運行應用程序的多個實例,理想情況下每個 CPU 內核上有一個實例,從而在實例之間分配負載和任務。

[圖片]

重要提示:由于應用程序實例作為單獨的進程運行,因此它們不共享相同的內存空間。 也就是說,對象對于應用程序的每個實例都是本地的。 因此,您無法在應用程序代碼中維護狀態(tài)。 但是,您可以使用像 Redis 這樣的內存中數(shù)據(jù)存儲來存儲與會話相關的數(shù)據(jù)和狀態(tài)。 這個警告基本上適用于所有形式的水平擴展,無論是多進程集群還是多物理服務器。

在集群應用程序中,工作進程可以單獨崩潰而不影響其余進程。 除了性能優(yōu)勢之外,故障隔離是運行應用進程集群的另一個原因。 每當工作進程崩潰時,請始終確保記錄該事件并使用 cluster.fork () 生成一個新進程。

Using PM2

如果使用 PM2 部署應用程序,則無需修改應用程序代碼即可利用集群。 您應該首先確保您的應用程序是無狀態(tài)的,這意味著沒有本地數(shù)據(jù)存儲在進程中(例如會話、websocket 連接等)。

當使用 PM2 運行應用程序時,您可以啟用集群模式以在具有您選擇的多個實例的集群中運行它,例如匹配機器上可用 CPU 的數(shù)量。 您可以使用 pm2 命令行工具手動更改集群中的進程數(shù),而無需停止應用程序。

要啟用集群模式,請像這樣啟動您的應用程序:

# Start 4 worker processes $ pm2 start npm --name my-app -i 4 -- start # Auto-detect number of available CPUs and start that many worker processes $ pm2 start npm --name my-app -i max -- start

這也可以在 PM2 進程文件(ecosystem.config.js 或類似文件)中通過將 exec_mode 設置為 cluster 并將實例設置為要啟動的工作程序數(shù)量來配置。

運行后,應用程序可以像這樣縮放:

# Add 3 more workers $ pm2 scale my-app +3 # Scale to a specific number of workers $ pm2 scale my-app 2

Cache request results

在生產(chǎn)中提高性能的另一個策略是緩存請求的結果,這樣您的應用程序就不會重復操作來重復處理相同的請求。

使用 Varnish 或 Nginx 等緩存服務器(另請參閱 Nginx 緩存)可以大大提高應用程序的速度和性能。

Use a load balancer

無論應用程序如何優(yōu)化,單個實例只能處理有限的負載和流量。 擴展應用程序的一種方法是運行它的多個實例并通過負載均衡器分配流量。 設置負載均衡器可以提高應用程序的性能和速度,并使其能夠比單個實例擴展更多。

負載均衡器通常是一個反向代理,用于協(xié)調進出多個應用程序實例和服務器的流量。 您可以使用 Nginx 或 HAProxy 輕松地為您的應用程序設置負載均衡器。

通過負載平衡,您可能必須確保與特定會話 ID 關聯(lián)的請求連接到發(fā)起它們的進程。 這稱為親緣會話或粘性會話,可以通過上面的建議解決,使用諸如 Redis 之類的數(shù)據(jù)存儲來存儲會話數(shù)據(jù)(取決于您的應用程序)。

Use a reverse proxy

反向代理位于 Web 應用程序的前面,除了將請求定向到應用程序之外,還對請求執(zhí)行支持操作。 它可以處理錯誤頁面、壓縮、緩存、提供文件和負載平衡等。

將不需要應用程序狀態(tài)知識的任務移交給反向代理可以釋放 Express 來執(zhí)行專門的應用程序任務。 出于這個原因,建議在生產(chǎn)中使用反向代理(如 Nginx 或 HAProxy)運行 Express。

總結

以上是生活随笔為你收集整理的使用 Node.js Express 的最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。