PM2發佈於2013年,是使用JavaScript開發,主要用於Node.js業務持久化的進程管理器。相對於Systemd、Supervisord等通用進程管理器,PM2對JavaScript的業務更爲友好,且使用更爲簡單,有着豐富的可擴展性,對非JavaScript業務的管理一樣出色。惋惜的是許多PM2用戶對PM2的瞭解並很少,大部分用戶都只掌握了基礎的進程管理,其實PM2的能力毫不止於此,充分使用PM2可以讓業務開發和維護的效率大大提高。本文就來列舉這樣十個PM2中冷門但實用的功能,但願可以幫助讀者對PM2有新的認識。
本文原載於 未命名小站,由做者本人同步至SegmentFault,轉載請註明原做者博客地址或本連接,謝謝!
提早說明:本文使用PM2的命令行模式講解,但如下內容對於API調用一樣有效,命令行和API之間的轉換規律能夠閱讀 官方文檔。
一般咱們但願PM2自己開機自啓,須要執行pm2 startup
讓其註冊到操做系統的服務管理工具中,若是咱們還但願PM2中的進程能隨着PM2啓動而啓動,就須要每次在新增或刪除進程後執行pm2 save
,但若是你是一個像筆者同樣記性很差的人,極可能會忘記執行這一步,致使PM2從新啓動後,一個業務都沒啓動。那麼這『多餘』的一步有沒有辦法能自動執行呢?答案是有的:javascript
pm2 set pm2:autodump true
在Shell中輸入這一行命令,咱們就開啓了PM2的自動保存功能,這樣子咱們對進程的變動將會被即時保存到~/.pm2/dump.pm2
中,無需手動執行pm2 save
。html
這裏咱們使用到了pm2 set
這個命令,其實這個命令執行的是對~/.pm2/module_conf.json
的修改。這個文件是PM2下各模塊的通用配置文件,在安裝其餘PM2模塊(如反向代理、負載均衡服務器等)一樣也有可能接觸到這個文件。但對於PM2自己來講,目前可供咱們使用的配置項只有autodump
、registry
、docker
這三個,且沒有集中的文檔對其進行描述,感興趣的讀者能夠閱讀這三個配置項在源碼中的實現,此處再也不贅述:前端
部分業務可能爲了省事將日誌直接輸出到stdout和stderr,在Shell中直接運行時咱們可使用如Linux和MacOS的重定向符>
來將stdout輸出到文件,再使用2>&1
將stderr輸出到stdout。但假設這樣一個『省事』的業務上了生產環境,咱們須要使用PM2來運行之,應該怎樣作才能看到日誌呢?PM2一樣爲咱們提供了日誌重定向的功能:java
pm2 start --log [fille] ...
是的,只須要在啓動進程時指定--log
參數,並提供日誌文件的路徑(是否存在這個文件都沒有關係),就能夠將stdout和stderr輸出到咱們指定的日誌文件了。接下來咱們就可使用如tail
一類的工具來對日誌進行跟蹤,或者你也可使用PM2自帶的日誌顯示功能:node
pm2 logs [id]
這裏的id是你在執行pm2 ps
時候所看到的進程id。webpack
也許你須要在PM2中運行一個內存管理比較差勁的程序,但又不但願這個程序在發生內存泄漏後消耗掉全部資源,影響其餘進程,這時候PM2的內存限制功能就能夠派上用場了:git
pm2 start --max-memory-restart=1024M ...
這裏的單位能夠爲K(iB)、M(iB)和G(iB),使用該參數啓動進程後,PM2就會在進程內存使用率超過限制時強制重啓進程,對於一些存在內存泄漏問題但不便於解決(或不必解決)的業務很是實用。github
一般咱們可使用pm2 ps
來查看當前正在運行的全部進程,但這一命令只顯示了最基礎的信息,如環境變量、運行入口、運行參數等信息並無在列表中顯示出來。那麼咱們應該如何查看這些信息呢?PM2提供了這樣的方法:web
pm2 show [id]
PM2會輸出關於這個進程的全部信息,以下圖所示:算法
上一節咱們提到了使用pm2 show
來查看某個進程的詳細信息,但在生產環境下咱們更多時候須要監控全部進程,包括CPU、內存使用、日誌輸出等信息,PM2一樣提供了以下的命令來幫助咱們監控全部進程:
pm2 monit
PM2會啓動一個面板,以下圖所示:
該面板能夠分爲四部分:進程列表(左上角)、當前進程日誌(右上角)、當前進程性能信息(左下角)、當前進程基礎信息(右下角)。咱們可使用鍵盤左右方向鍵來切換面板,使用上下方向鍵在面板中滾動,對全部進程進行監控。
實際上PM2 Plus還額外提供了資源佔用歷史、內存/CPU詳細分析(Profiling)等高級功能,但因爲該功能須要付費使用,此處再也不展開說明,若是願意付費使用的讀者能夠查閱官方文檔:PM2 Plus documentation。
剛剛咱們談了那麼多『不規範』的業務(如日誌輸出到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.js
和error.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
而非--disable-source-map
,筆者提交了相關修訂: pull/185。
pm2 start ./error.min.js --disable-source-map-support
能夠看到錯誤信息只顯示出如今第一行(但顯然問題並非出在第一行)。
接下來咱們開啓SourceMap支持,再運行一遍:
pm2 start ./error.min.js --source-map-support
此時就能從日誌中看到正常的調用棧信息了,幫助咱們更高效的跟蹤問題的來源。
在業務開發與測試過程當中,咱們常常會遇到文件更新後須要重啓業務的狀況。對於本地環境咱們可使用如Webpack Dev Server等工具監聽文件變化,而後在文件發生改變後從新運行服務器。而PM2一樣提供了相似的功能幫助咱們實現這一需求:
pm2 start --watch
這樣只要當前目錄下有任意文件發生改變,PM2都會嘗試重啓進程。
在使用這一參數的時候,有幾個須要注意的地方:
--watch
參數後,就算你手動中止進程(不刪除),進程也會在文件發生改變後自動啓動,解決該問題的方法是在中止進程的時候加入以下參數:pm2 stop [id] --watch
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 } }] }
相信不少使用過PM2或Docker的讀者都遇到業務出現運行時錯誤後不斷自動重啓的問題。但不少時候運行時錯誤並不是來自於業務自己,好比數據庫服務器中斷、鏈接數過多、甚至是上文提到的--watch
參數過於靈敏(不少IDE或編輯器支持自動保存,可能保存的版本還沒有開發完成,存在語法錯誤)。那麼有沒有諸如延時重啓、無縫重啓等功能呢?PM2提供了大量的相關選項:
pm2 start --restart-delay=2000 ...
這裏的2000單位爲毫秒,即在須要重啓的時候等待兩秒鐘。
更多時候咱們須要的是不斷延長的重啓時間,好比Filezilla鏈接FTP客戶端失敗後的重試時間會隨着重試次數增多不斷延長。PM2也提供了這樣的功能:
pm2 start --exp-backoff-restart-delay=1000
此處的1000單位也是毫秒,PM2會在屢次重啓失敗後以設定的時間爲初始值,使用指數移動平均算法不斷延長重試時間,最高爲15000毫秒(即15秒),並在進程成功啓動30秒後重置重試時間到到初始值。該算法的具體實現能夠參考PM2的相關源碼:
重啓老是須要耗時的,若是咱們但願業務在重啓的時候不中斷,就像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
參數。
有時候咱們還會使用pm2來進行一些儘管耗時,但不須要一直在後臺運行的業務,例如爬蟲。PM2默認會在進程退出後從新啓動,但也提供了參數幫助咱們關閉此功能:
pm2 start --no-autorestart ...
使用這個參數後,在業務退出時,狀態會直接變爲stop,而不會自動重啓。
上一節咱們提到了可使用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
的參數就能夠,以下所示:
P.S. 這裏的吃乎只是筆者胡謅的一個業務,寫完以後順手搜索了一下,沒想到真有一個網站叫作吃乎: 吃乎 - 發現身邊的美食。若有雷同,純屬巧合哈😂。
最後筆者想介紹一個極爲實用但極少有人說起的功能:HTTP服務器。以前和不少同窗探討大前端項目先後端分離的時候,發現他們大多都使用Nginx、Apache甚至Tomcat來託管前端的靜態頁面,而後使用PM2來託管後端API。但爲了一個簡單的前端頁面專門撰寫一堆配置文件,實在是太浪費時間了,PM2可能也發現了這一點,因而貼心的內置了HTTP服務器:
pm2 serve [path] [port]
是的,就這麼簡單,一條命令就能啓動一個HTTP服務器。因爲這個HTTP服務器使用的是Node.js實現,所以性能一樣很是優異,在大部分狀況下足夠使用。若是是負載很是大的業務,通常也不會考慮使用PM2,而會使用更具擴展性的Kubernetes。
以上是筆者所分享的十個PM2中冷門但實用的功能。其實這十個功能在閱讀官方文檔和源碼的過程當中均可以瞭解到,而在學習過程當中總結、掌握這些小技巧對於實際使用過程當中的效率提高有着很是大的幫助。
限於篇幅,一些更爲高級的功能(如Load&Dump、PM2 Plus)等並未在本文中說起。若是讀者們想了解更多相關內容,不要猶豫,立刻去閱讀文檔和相關源碼吧!相信你們在閱讀完畢後,必定會有比本文更多的收穫,靜心閱讀永遠是最高效、最深入、最細緻的學習方式。