淺析PM2的十個實用功能:自動保存、自定義日志文件、設置內存限制、查看進程信息詳細、監控所有進程、使用SourceMap定位錯誤位置、監聽目錄文件變化自動重啟、4種失敗重啟策略(零延遲高可用)、命名空間對服務歸類、內置http服務器


1、自動保存

  通常我們希望PM2本身開機自啟,需要執行 pm2 startup 讓其注冊到操作系統的服務管理工具中。

  如果我們還希望PM2中的進程能隨着PM2啟動而啟動,就需要每次在新增或刪除進程后執行 pm2 save;

  但如果你是一個像筆者一樣記性不好的人,很可能會忘記執行這一步,導致PM2重新啟動后,一個業務都沒啟動。那么這『多余』的一步有沒有辦法能自動執行呢?答案是有的:

pm2 set pm2:autodump true

  在Shell中輸入這一行命令,我們就開啟了PM2的自動保存功能,這樣子我們對進程的變更將會被即時保存到 ~/.pm2/dump.pm2 中,無需手動執行pm2 save

  這里我們使用到了 pm2 set 這個命令,其實這個命令執行的是對 ~/.pm2/module_conf.json 的修改。這個文件是PM2下各模塊的通用配置文件,在安裝其他PM2模塊(如反向代理、負載均衡服務器等)同樣也有可能接觸到這個文件。但對於PM2本身來說,目前可供我們使用的配置項只有 autodumpregistrydocker 這三個,且沒有集中的文檔對其進行描述,感興趣的讀者可以閱讀這三個配置項在源碼中的實現,此處不再贅述:

2、輸出日志到文件

  部分業務可能為了省事將日志直接輸出到 stdout 和 stderr,在Shell中直接運行時我們可以使用如Linux和MacOS的重定向符 > 來將stdout輸出到文件,再使用 2>&1 將stderr輸出到stdout。但假設這樣一個『省事』的業務上了生產環境,我們需要使用PM2來運行之,應該怎樣做才能看到日志呢?

  PM2同樣為我們提供了日志重定向的功能:

pm2 start --log [file] ...

  只需要在啟動進程時指定 --log 參數,並提供日志文件的路徑(是否存在這個文件都沒有關系),就可以將stdout和stderr輸出到我們指定的日志文件了。接下來我們就可以使用如 tail 一類的工具來對日志進行跟蹤,或者你也可以使用PM2自帶的日志顯示功能:

pm2 logs [id] // 這里的id是你在執行 pm2 ps 時候所看到的進程id。

3、設置內存限制

  也許你需要在PM2中運行一個內存管理比較差勁的程序,但又不希望這個程序在發生內存泄漏后消耗掉所有資源,影響其他進程,這時候PM2的內存限制功能就可以派上用場了:

pm2 start --max-memory-restart=1024M ...

  這里的單位可以為K(iB)、M(iB)和G(iB),使用該參數啟動進程后,PM2就會在進程內存使用率超過限制時強制重啟進程,對於一些存在內存泄漏問題但不便於解決(或沒必要解決)的業務非常實用。

4、查看某個進程的信息

  通常我們可以使用 pm2 ps 來查看當前正在運行的所有進程,但這一命令只顯示了最基礎的信息,如環境變量、運行入口、運行參數等信息並沒有在列表中顯示出來。那么我們應該如何查看這些信息呢?PM2提供了這樣的方法:

pm2 show [id]

  PM2會輸出關於這個進程的所有信息,如下圖所示:

5、使用總覽面板監控所有進程

  上一節我們提到了使用 pm2 show 來查看某個進程的詳細信息,但在生產環境下我們更多時候需要監控所有進程,包括CPU、內存使用、日志輸出等信息,PM2同樣提供了如下的命令來幫助我們監控所有進程:

pm2 monit

  PM2會啟動一個面板,如下圖所示:

  該面板可以分為四部分:進程列表(左上角)、當前進程日志(右上角)、當前進程性能信息(左下角)、當前進程基礎信息(右下角)。我們可以使用鍵盤左右方向鍵來切換面板,使用上下方向鍵在面板中滾動,對所有進程進行監控。

  實際上PM2 Plus還額外提供了資源占用歷史、內存/CPU詳細分析(Profiling)等高級功能,但由於該功能需要付費使用,此處不再展開說明,如果願意付費使用的讀者可以查閱官方文檔:PM2 Plus documentation

6、使用 SourceMap 獲取錯誤位置

  剛剛我們談了那么多『不規范』的業務(如日志輸出到stdout、內存泄漏等),接下來我們舉一個『規范』的例子,也就是使用Webpack(或其他構建工具)對JavaScript代碼進行壓縮后的線上業務。

  如果這些業務在線上出現了錯誤,但由於代碼被壓縮,只能顯示錯誤出現在第一行(本來就只有一行),我們要怎樣才能在日志中看到更詳細的信息呢?

  PM2考慮到了這一點,並提供了自動加載 SourceMap 的功能:

pm2 start --source-map-support ...

  假設你加載的js文件是index.js,在開啟SourceMap支持后,PM2會自動尋找同目錄下的index.js.map,並在出現錯誤時加載之,在日志中輸出更易讀的錯誤日志。下面是一個例子:

// error.js
setInterval(function() { triggerError(); }, 1000); function triggerError() { throw new Error("Some error..."); }

  這里筆者使用Webpack生成了對應的error.min.jserror.min.js.map文件:

webpack ./error.js -o error.min.js --devtool source-map --output-source-map-filename error.min.js.map

  然后使用 pm2 加載error.min.js(不開啟SourceMap):關閉SourceMap支持使用 --disable-source-map-support

pm2 start ./error.min.js --disable-source-map-support

  可以看到錯誤信息只顯示出現在第一行(但顯然問題並不是出在第一行)。

  接下來我們開啟SourceMap支持,再運行一遍:

pm2 start ./error.min.js --source-map-support

  此時就能從日志中看到正常的調用棧信息了,幫助我們更高效的跟蹤問題的來源。

7、業務更新時自動重啟進程

  在業務開發與測試過程中,我們經常會遇到文件更新后需要重啟業務的情況。對於本地環境我們可以使用如Webpack Dev Server等工具監聽文件變化,然后在文件發生改變后重新運行服務器。而PM2同樣提供了類似的功能幫助我們實現這一需求:

pm2 start --watch

  這樣只要當前目錄下有任意文件發生改變,PM2都會嘗試重啟進程。

  在使用這一參數的時候,有幾個需要注意的地方:

(1)請在程序所在的目錄執行啟動命令,否則將會監視的不是程序所在目錄,而是你執行目錄當前所在的目錄。

(2)開啟--watch參數后,就算你手動停止進程(不刪除),進程也會在文件發生改變后自動啟動,解決該問題的方法是在停止進程的時候加入如下參數:

pm2 stop [id] --watch

(3)如果我們需要忽略一些目錄的變化(如臨時文件)或只監聽某些目錄的變化,就需要使用PM2的API:PM2 Ecosystem,然后撰寫如下配置文件到ecosystem.config.js(摘自官方文檔):

module.exports = { apps: [{ script: "app.js", watch: ["server", "client"], // Delay between restart
    watch_delay: 1000, ignore_watch : ["node_modules", "client/img"], watch_options: { "followSymlinks": false } }] }

8、更聰明的失敗重啟策略

  相信很多使用過PM2或Docker的讀者都遇到業務出現運行時錯誤后不斷自動重啟的問題。但很多時候運行時錯誤並非來自於業務本身,比如數據庫服務器中斷、連接數過多、甚至是上文提到的--watch參數過於靈敏(很多IDE或編輯器支持自動保存,可能保存的版本尚未開發完成,存在語法錯誤)。那么有沒有諸如延時重啟、無縫重啟等功能呢?PM2提供了大量的相關選項:

(1)固定延時

pm2 start --restart-delay=2000 ... // 這里的2000單位為毫秒,即在需要重啟的時候等待兩秒鍾。

(2)靈活延時

  更多時候我們需要的是不斷延長的重啟時間,比如Filezilla連接FTP客戶端失敗后的重試時間會隨着重試次數增多不斷延長。PM2也提供了這樣的功能:

pm2 start --exp-backoff-restart-delay=1000

  此處的1000單位也是毫秒,PM2會在多次重啟失敗后以設定的時間為初始值,使用指數移動平均算法不斷延長重試時間,最高為15000毫秒(即15秒),並在進程成功啟動30秒后重置重試時間到到初始值。該算法的具體實現可以參考PM2的相關源碼:

(3)零延時高可用

  重啟總是需要耗時的,如果我們希望業務在重啟的時候不中斷,就像Kubernetes的滾動更新一樣,那應該怎么做呢?PM2的集群模式可以幫助我們實現這一需求。

  需要注意的是,這里我們所指的『集群』並非是Kubernetes這樣邏輯獨立的服務器集群,而是Node.js原生支持的Cluster組件。如果對Cluster組件不了解的讀者可以閱讀這篇Node.js官方文檔:Cluster | Node.js v14.2.0 Documentation

  簡而言之,Cluster組件就類似於PHP中的FPM或是Nginx中的Worker,為單線程的JavaScript運行時增加了能在多CPU上並行接收請求的能力,即運行多個實例作為子進程,並由一個父進程負責請求的調度。

  但就算你不懂Cluster組件或是不想為現有業務加入Cluster支持,也沒有關系,因為PM2為你實現了它,這樣我們無需對現有源碼進行任何修改,也能充分利用Cluster組件的功能來實現高可用,任意一個進程的停止,不會對整個業務造成影響。這就是本節筆者要提到的『零延時高可用』。

  這里筆者以一個非常簡單的Web服務器為例:

var http = require("http"); http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World\n'); }).listen(8081);

  然后我們使用如下命令啟動包含四個進程的Cluster(因為筆者的電腦剛好是四個核心)

pm2 start ./server.js -i 4

  這里的4就是進程數,也可以設置為max以匹配當前環境最大核心數。

  接下來我們就能在pm2 ps的結果中看到如下四個子進程:

  需要注意,Cluster 模式下重啟業務需要使用 reload,而且不能使用進程ID(因為我們需要重啟的是一組進程而非一個),如下所示:

pm2 reload server

  這里的server是上面 pm2 ps 結果中進程組共有的name。這樣就既能充分利用服務器性能,又能實現業務的高可用,而我們所需要耗費的只是額外添加一個 -i 參數。

(4)關閉失敗重啟功能

  有時候我們還會使用pm2來進行一些盡管耗時,但不需要一直在后台運行的業務,例如爬蟲

  PM2默認會在進程退出后重新啟動,但也提供了參數幫助我們關閉此功能:

pm2 start --no-autorestart ...

  使用這個參數后,在業務退出時,狀態會直接變為stop,而不會自動重啟。

9、一條命令操作一組業務

  上一節我們提到了可以使用Cluster批量生成進程並對其進行管理,但Cluster只是生成了一批一模一樣的進程。一個常規的業務(尤其是微服務當道的現在)可能會由多個進程組成。這里筆者假設有一個業務,包含API(api.js)、服務端渲染(ssr.js)、數據庫(db.js)、監控(monitor.js)四個組件,如何對它們進行批量管理(比如重啟)呢?

  細心的讀者可能在上面 pm2 ps 的輸出結果中看到了namespace的字段,默認為default,其實這就是本節要說的關鍵內容:命名空間。

  我們可以使用命名空間對同一類業務進行歸類,然后按命名空間來對業務進行批量管理

pm2 start api.js --namespace chihu pm2 start ssr.js --namespace chihu pm2 start db.js --namespace chihu pm2 start monitor.js --namespace chihu

  這時候我們可以看到,這四個進程的namespace均為chihu。如果我們需要停止這四個業務,就不需要逐一停止,只需要執行一條命令:

pm2 stop chihu

  這樣就能一次停止四個進程了。其他操作同樣類似,只要在對應命令的--help面板中能看到namespace的參數就可以,如下所示:

10、PM2的內置HTTP服務器

  最后筆者想介紹一個極為實用但極少有人提及的功能:HTTP服務器。

  之前和很多同學探討大前端項目前后端分離的時候,發現他們大多都使用Nginx、Apache甚至Tomcat來托管前端的靜態頁面,然后使用PM2來托管后端API。但為了一個簡單的前端頁面專門撰寫一堆配置文件,實在是太浪費時間了,PM2可能也發現了這一點,於是貼心的內置了HTTP服務器:

pm2 serve [path] [port]

  是的,就這么簡單,一條命令就能啟動一個HTTP服務器。

  由於這個HTTP服務器使用的是Node.js實現,因此性能同樣非常優異,在大部分情況下足夠使用。如果是負載非常大的業務,一般也不會考慮使用PM2,而會使用更具擴展性的Kubernetes。

原文作者:路人甲的世界

原文鏈接:https://zhuanlan.zhihu.com/p/139116801


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM