摘要:就在幾周前,我正在和一個朋友談話,他不經意間提到,你永遠都不會在生產中直接使用來運行程序。我強烈點頭,表示我也不會在生產中直接運行,原因可能每個人都知道。首先,讓我們弄清楚永遠不要在生產中直接通過運行程序的說法。
原文地址:You should never ever run directly against Node.js in production. Maybe.
原文作者:Burke Holland
譯文出自:掘金翻譯計劃
本文永久鏈接:github.com/xitu/gold-m…
譯者:fireairforce
校對者:HearFishle, JasonLinkinBright
有時候我也在想我是否真的知道很多東西。
就在幾周前,我正在和一個朋友談話,他不經意間提到,“你永遠都不會在生產中直接使用 Node 來運行程序”。我強烈點頭,表示我也不會在生產中直接運行 Node,原因可能每個人都知道。但是我并不知道,我應該知道原因嗎?我還能繼續寫代碼嗎?
如果讓我繪制一個維恩圖來表示我所知道的和其他人都知道的東西,它看起來就像這樣:
順便一提,我年紀越大,那個小點就會越小。
Alicia Liu 創建了一個更好的圖表,改變了我的生活。她說這種情況更像是...
我非常喜歡這個圖表,因為我希望它是真實的。我不想把余生都過得像一個微不足道的萎縮藍點一樣。
太戲劇化了。怪潘多拉。當我寫這篇文章的時候,我無法控制接下來要發生什么。而 Dashboard Confessional 真是一副毒藥。
好吧,假設 Alicia 的圖表是真的,我想與大家分享一下我現在對在生產中運行 Node 應用程序的一些認識。也許在這個問題上我們的相對維恩圖沒有重疊。
首先,讓我們弄清楚“永遠不要在生產中直接通過 Node 運行程序”的說法。
永遠不要直接運行生產中的 Node這個說法可能對,也可能不對。我們一起來探討下這個說法是怎么得到的。首先,讓我們看看為什么不對。
假設我們有一個簡單的 Express 服務器。我能想到的最簡單的 Express 服務器如下:
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
// 查看 http://localhost:3000
app.get("/", function(req, res) {
res.send("Again I Go Unnoticed");
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
通過定義在 package.json 中的啟動腳本來運行它。
"scripts": {
"start": "node index.js",
"test": "pfffft"
}
這里會有兩個問題。第一個是開發問題,還有一個是生產問題。
開發問題指的是當我們修改代碼的時候,我們必須停止并且再次啟動應用程序來得到我們更改后的效果。
我們通常會使用某種 Node 進程管理器,例如 supervisor 或 nodemon 來解決這個問題。這些包會監聽我們的項目,并會在我們提交更改時重啟服務器。我通常都會這樣做。
"scripts": {
"dev": "npx supervisor index.js",
"start": "node index.js"
}
然后運行 npm run dev。請注意,我在這里運行 npx supervisor,它允許我們在未安裝 supervisor 包的情況下使用它。
我們的另外一個問題是我們仍然直接在針對 Node 運行,我們已經說過這很糟糕,現在我們將要找出原因。
我要在這里添加另一個路由,嘗試從不存在的磁盤讀取文件。這是一個很容易在任何實際應用程序中出現的錯誤。
const express = require("express");
const app = express();
const fs = require("fs");
const port = process.env.PORT || 3000;
// 查看 http://localhost:3000
app.get("/", function(req, res) {
res.send("Again I Go Unnoticed");
});
app.get("/read", function(req, res) {
// 這個路由不存在
fs.createReadStream("my-self-esteem.txt");
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
如果我們直接針對 Node 運行 npm start 并且導航到 read 端點,頁面會報錯,因為這個文件不存在。
這沒什么大不了的,對吧?這只是一個錯誤,碰巧發生了。
不。這很重要。如果你返回終端,你會看到應用程序已經完全關閉了。
這意味著,如果你回到瀏覽器并嘗試訪問站點的根 URL,你會得到相同的錯誤頁面。一個方法中的錯誤導致應用中所有的路由都失效了。
這是很糟糕的。這就是人們說 “永遠都不要直接在生產中運行 Node”
好。如果我們無法在生產中直接運行 Node,那么在生產中運行 Node 的正確方法是什么?
生產中 Node 的選項我們有幾個選擇。
其中之一就是在生產中簡單地使用類似 supervisor 或 nodemon 的工具,就像我們在開發中使用它們一樣。這可行,但這些工具有點輕量級。更好的選擇是 pm2。
pm2 支援pm2 是一個 Node 進程管理器,有很多有用的功能。就像其他的 “JavaScript” 庫一樣,你可以使用 npm 全局安裝它 —— 或者你也可以再次使用 npx。這里不再贅述。
有很多使用 pm2 運行程序的方法。最簡單的就是在入口文件調用 pm2 start。
"scripts": {
"start": "pm2 start index.js",
"dev": "npx supervisor index.js"
},
終端會顯示這些內容:
這是我們在 pm2 監控的后臺運行的進程。如果你訪問 read 端點把程序搞崩了,pm2 將自動重新啟動它。你在終端不會看到任何內容,因為它在后臺運行。如果你想看到 pm2 正在做什么,你可以運行 pm2 log 0。這個 0 是我們想要查看日志的進程的 ID。
接下來!你可以看到,pm2 會在應用程序由于未處理的錯誤而停機時重新啟動應用程序。
我們還可以提取開發命令,并為我們準備PM2監視文件,在任何更改發生時重新啟動程序。
"scripts": {
"start": "pm2 start index.js --watch",
"dev": "npx supervisor index.js"
},
請注意,因為 pm2 在后臺運行,所以你不能只是通過 ctrl+c 來終止正在運行的 pm2 進程。你必須通過傳遞進程的 ID 或者名稱來停止進程。
pm2 stop 0
pm2 stop index
另請注意,pm2 會保存對進程的引用,以便你可以重新啟動它。
如果要刪除該進程引用,則需要運行 pm2 delete。你可以使用一個命令 delete 來停止和刪除進程。
pm2 delete index
我們也可以使用 pm2 來運行我們應用程序的多個進程。pm2 會自動平衡這些實例的負載。
使用 pm2 fork 模式的多個進程pm2 有很多配置選項,它們包含在一個 “ecosystem” 文件中。可以通過運行 pm2 init來創建一個。你會得到以下的內容:
module.exports = {
apps: [
{
name: "Express App",
script: "index.js",
instances: 4,
autorestart: true,
watch: true,
max_memory_restart: "1G",
env: {
NODE_ENV: "development"
},
env_production: {
NODE_ENV: "production"
}
}
]
};
本文不會去講關于“部署”的內容,因為我對“部署”也不太了解。
“應用程序”部分定義了希望 pm2 運行和監視的應用程序。你可以運行不止一個應用程序。許多這些配置設置可能是不言而喻的。這里我要關注的是實例設置。
pm2 可以運行你的應用程序的多個實例。你可以傳入多個你想運行的實例,pm2 都可以啟動它們。因此,如果我們想運行 4 個實例,我們可以創建以下配置文件。
module.exports = {
apps: [
{
name: "Express App",
script: "index.js",
instances: 4,
autorestart: true,
watch: true,
max_memory_restart: "1G",
env: {
NODE_ENV: "development"
},
env_production: {
NODE_ENV: "production"
}
}
]
};
然后我們使用 pm2 start 來運行它。
pm2 現在會以“集群”模式運行。這些進程中的每一個都會在我的計算機上的不同 CPU 上運行,具體取決于我擁有的內核數量。如果我們想在不知道擁有多個內核的情況下為每個內核運行一個進程,我們就可以將 max 參數傳遞給 instances 參數來進行配置。
{
...
instances: "max",
...
}
讓我們看看我的這臺機器上有幾個內核。
8個內核!哇。我要在我的微軟發行的機器上安裝 Subnautica。別告訴他們我說過。
在分隔的 CPU 上運行進程的好處是,即使有一個進程運行異常,占用了 100% 的 CPU,其他進程依然可以保持運行。pm2 將根據需要將 CPU 上的進程加倍。
你可以使用 pm2 進行更多操作,包括監視和以其他方式處理那些討厭的 environment variables。
另外一個注意事項:如果由于某種原因,你希望 pm2 運行 npm start 指令。你可以通過運行 npm 作為進程并傳遞 -- start。“start” 之前的空格在這里非常重要。
pm2 start npm -- start
在 Azure AppService 中,默認在后臺包含 pm2。如果要在 Azure 中使用 pm2,則無需將其包含在 package.json 文件中。你可以添加一個 ecosystem 文件,然后就可以使用了。
好!既然我們已經了解了關于 pm2 的所有內容,那么讓我們談談為什么你可能不想使用它,而且它可能確實可以直接針對 Node 運行。
在生產中直接針對 Node 運行我對此有一些疑問,所以我聯系了 Tierney Cyren,它是巨大的橙色知識圈的一部分,特別是在 Node 方面。
Tierney 指出使用基于 Node 的進程管理器(如 pm2)有一些缺點。
主要原因是不應該使用 Node 來監視 Node。你肯定不希望用你正在監視的東西監視它自己。就像你在周五晚上讓我十幾歲的兒子監督他自己一樣:結果會很糟糕嗎?可能,也可能不會。。
Tierney 建議你不要使用 Node 進程管理器來運行應用程序。相反,在更高級別上有一些東西可以監視應用程序的多個多帶帶實例。例如,一個理想的設置是,如果你有一個 Kubernetes 集群,你的應用程序在不同的容器上運行。然后 Kubernetes 可以監控這些容器,如果其中任何容器發生故障,它可以將它們恢復并且報告健康狀況。
在這種情況下,你可以直接針對Node運行,因為你正在更高級別進行監視。
事實證明,Azure 已經在做這件事了。如果我們不將 pm2 ecosystem 文件推送到 Azure,它將使用我們的 package.json 文件運行腳本來啟動應用程序,我們可以直接針對Node運行。
"scripts": {
"start": "node index.js"
}
在這種情況下,我們直接針對 Node 運行,沒關系。如果應用程序崩潰,你會發現它又回來了。那是因為在 Azure 中,你的應用程序在一個容器中運行。Azure 負責容器調度,并知道何時去更新。
但這里仍然只有一個實例。容器崩潰后需要一秒鐘才能重新聯機,這意味著你的用戶可能會有幾秒鐘的停機時間。
理想情況下,你希望運行多個容器。解決方案是將應用程序的多個實例部署到多個 Azure AppService 站點,然后使用 Azure Front Door 在單個 IP 地址下對應用程序進行負載均衡。Front Door 知道容器何時關閉,并且將流量路由到應用程序的其他健康實例。
Azure Front Door Service | Microsoft Azure
使用 Azure Front Door 交付,保護和跟蹤全球分布式微服務應用程序的性能
Tierney 的另一個建議是使用 systemd 運行 Node。我不是很了解(或者說根本不知道)systemd,我已經把這句話弄錯過一次了,所以我會用 Tierney 自己的原話來表述:
只有在部署中訪問 Linux 并控制 Node 在服務級別上啟動的方式時,才有可能實現此選項。如果你在一個長時間運行的 Linux 虛擬機中運行 node.js 進程,比如說 Azure 虛擬機,那么使用 systemd 運行 node.js 是個不錯的選擇。如果你只是將文件部署到類似于 Azure AppService 或 Heroku 的服務中,或者運行在類似于 Azure 容器實例的容器化環境中,那么你可以避開此選項。
使用 Systemd 運行 Node.js 應用程序 —— 第一部分
你已經在 Node 中編寫了下一個很棒的應用程序,并且你已經準備發布它。這意味著你可以...
Node.js 工作線程Tierney 還希望你知道 Node 中有工作線程,這可以讓你在多個線程上啟動你的應用程序,從而無需像 pm2 這樣的東西。也許吧。我不知道,我沒看過這篇文章。
Node.js v11.14.0 Documentation
The worker_threads module enables the use of threads that execute JavaScript in parallel. To access it: const worker =…
做個成熟的開發者Tierney 的最后一個建議就是像一個成熟的開發者一樣處理錯誤和編寫測試。但是誰有時間呢?
小圓圈永駐現在你知道這個藍色小圓圈里的大部分東西了。剩下的只是關于 emo 樂隊和啤酒的無用事情。
有關 pm2, Node 和 Azure 的更多信息,請查看以下資源:
pm2.keymetrics.io/
VS Code 上的 Node.js 部署
將簡單的 Node 站點部署到 Azure
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改并 PR,也可獲得相應獎勵積分。文章開頭的 本文永久鏈接 即為本文在 GitHub 上的 MarkDown 鏈接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、后端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6813.html
摘要:原文引言這篇文檔包含了如何避免使代碼性能遠低于預期的建議尤其是一些會導致牽涉到等無法優化相關函數的問題一些背景在中并沒有解釋器但卻有兩個不同的編譯器通用編譯器和優化編譯器這意味著你的代碼總是會被編譯為機器碼后直接運行這樣一定很快咯并不是 原文:http://dev.zm1v1.com/2015/08/19/javascript-optimization-killers/引言 這篇文檔包...
摘要:前言這將是一個分為兩部分,內容是關于在生產環境下,跑應用的最佳實踐。第一部分會關注安全性,第二部分則會關注性能和可靠性。關于第一部分,請參閱在生產環境下的最佳實踐安全性。 前言 這將是一個分為兩部分,內容是關于在生產環境下,跑Express應用的最佳實踐。第一部分會關注安全性,第二部分則會關注性能和可靠性。當你讀這篇文章時,會假設你已經對Node.js和web開發有所了解,并且對生產環...
摘要:教程如何使用打包通過這個系列教程一步一步學習如何使用更小更快的取代和打包文件。安裝并且創建配置文件。提示是告訴我們實際需要哪些插件的集合。通過下面的命令安裝兩個插件更新然后,引入插件并添加進配置注意屬性是為了幫助模塊遷移到的一部分。 教程:如何使用Rollup打包JavaScript 通過這個系列教程一步一步學習如何使用更小更快的Rollup取代webpack和Browserify打包...
摘要:前言這將是一個分為兩部分,內容是關于在生產環境下,跑應用的最佳實踐。潛在的攻擊者可以通過它們進行針對性的攻擊。 前言 這將是一個分為兩部分,內容是關于在生產環境下,跑Express應用的最佳實踐。第一部分會關注安全性,第二部分最會關注性能和可靠性。當你讀這篇文章時,假設你已經對Node.js和web開發有所了解,并且對生產環境有了概念。 概覽 生產環境,指的是軟件生命循環中的某個階段。...
摘要:定時器在和瀏覽器中的表現形式是相同的。關于定時器的一個重要的事情是,我們提供的延遲不代表在這個時間之后回調就會被執行。它的真正含義是,一旦主線程完成所有操作包括微任務并且沒有其它具有更高優先級的定時器,將在此時間之后執行回調。 眾成翻譯 原文鏈接 關于作者 2018年6月21日出版? 本指南面向了解Javascript但尚未十分熟悉Node.js的前端開發人員。我這里不專注于語言本身...
閱讀 2473·2021-11-16 11:45
閱讀 2442·2021-10-11 10:59
閱讀 2250·2021-10-08 10:05
閱讀 3813·2021-09-23 11:30
閱讀 2369·2021-09-07 09:58
閱讀 789·2019-08-30 15:55
閱讀 772·2019-08-30 15:53
閱讀 1922·2019-08-29 17:00