<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          10分鐘快速過一遍PM2核心知識點

          共 4938字,需瀏覽 10分鐘

           ·

          2020-10-14 17:00


          授權(quán)轉(zhuǎn)載自:Jiasm

          https://juejin.im/post/6866081343454773262

          近期有需求需要了解 PM2 一些功能的實現(xiàn)方式,所以趁勢看了一下 PM2 的源碼,也算是用了這么多年的 PM2,第一次進入內(nèi)部進行一些探索。
          PM2 是一個 基于 node.js 的進程管理工具,本身 node.js 是一個單進程的語言,但是 PM2 可以實現(xiàn)多進程的運行及管理(當然還是基于 node 的 API),還提供程序系統(tǒng)信息的展示,包括 內(nèi)存、CPU 等數(shù)據(jù)。

          PM2 的核心功能概覽

          源碼位置:

          https://github.com/Unitech/pm2)

          官方網(wǎng)站:

          https://pm2.keymetrics.io/

          PM2 的功能、插件非常的豐富,但比較核心的功能其實不多:

          1. 多進程管理
          2. 系統(tǒng)信息監(jiān)控
          3. 日志管理

          其他的一些功能就都是基于 PM2 之上的輔助功能了。

          項目結(jié)構(gòu)

          PM2 的項目結(jié)構(gòu)算是比較簡潔的了,主要的源碼都在 lib 目錄下, God 目錄為核心功能多進程管理的實現(xiàn),以及 API 目錄則是提供了各種能力,包括 日志管理、面板查看系統(tǒng)信息以及各種輔助功能,最后就是 Sysinfo 目錄下關(guān)于如何采集系統(tǒng)信息的實現(xiàn)了。

          #?刪除了多個不相干的文件、文件夾
          lib
          ├──?API?????#?日志管理、GUI?等輔助功能
          ├──?God?????#?多進程管理邏輯實現(xiàn)位置
          └──?Sysinfo?#?系統(tǒng)信息采集

          幾個比較關(guān)鍵的文件作用:

          • Daemon.js
            • 守護進程的主要邏輯實現(xiàn),包括 rpc server,以及各種守護進程的能力
          • God.js
            • 業(yè)務進程的包裹層,負責與守護進程建立連接,以及注入一些操作,我們編寫的代碼最終是由這里執(zhí)行的
          • Client.js
            • 執(zhí)行 PM2 命令的主要邏輯實現(xiàn),包括與守護進程建立 rpc 連接,以及各種請求守護進程的操作
          • API.js
            • 各種功能性的實現(xiàn),包括啟動、關(guān)閉項目、展示列表、展示系統(tǒng)信息等操作,會調(diào)用 Client 的各種函數(shù)
          • binaries/CLI.js
            • 執(zhí)行 pm2 命令時候觸發(fā)的入口文件

          守護進程與 Client 進程通訊方式

          看源碼后會知道,PM2 與 Client 進程(也就是我們 pm2 start XXX 時對應的進程),是通過 RPC 進行通訊的,這樣就能保證所有的 Client 進程可以與守護進程進行通訊,上報一些信息,以及從守護進程層面執(zhí)行一些操作。

          PM2 啟動程序的方式

          PM2 并不是簡單的使用 node XXX 來啟動我們的程序,就像前邊所提到了守護進程與 Client 進程的通訊方式,Client 進程會將啟動業(yè)務進程所需要的配置,通過 rpc 傳遞給守護進程,由守護進程去啟動程序。
          這樣,在 PM2 start 命令執(zhí)行完成以后業(yè)務進程也在后臺運行起來了,然后等到我們后續(xù)想再針對業(yè)務進程進行一些操作的時候,就可以通過列表查看對應的 pid、name 來進行對應的操作,同樣是通過 Client 觸發(fā) rpc 請求到守護進程,實現(xiàn)邏輯。

          當然,我們其實很少會有單獨啟動守護進程的操作,守護進程的啟動其實被寫在了 Client 啟動的邏輯中,在 Client 啟動的時候會檢查是否有存活的守護進程,如果沒有的話,會嘗試啟動一個新的守護進程用于后續(xù)的使用。
          具體方式就是通過 spawn + detached: true 來實現(xiàn)的,創(chuàng)建一個單獨的進程,這樣即便是我們的 Client 作為父進程退出了,守護進程依然是可以獨立運行在后臺的。

          P.S. 在使用 PM2 的時候應該有時也會看到有些這樣的輸出,這個其實就是 Client 運行時監(jiān)測到守護進程還沒有啟動,主動啟動了守護進程:

          >?[PM2]?Spawning?PM2?daemon?with?pm2_home=/Users/jiashunming/.pm2
          >?[PM2]?PM2?Successfully?daemonized

          多進程管理

          一般使用 PM2 實現(xiàn)多進程管理主要的目的是為了能夠讓我們的 node 程序可以運行在多核 CPU 上,比如四核機器,我們就希望能夠存在四個進程在運行,以便更高效的支持服務。
          在進程管理上,PM2 提供了一個大家經(jīng)常會用到的參數(shù):exec_mode,它的取值只有兩個,clusterforkfork 是一個比較常規(guī)的模式,相當于就是執(zhí)行了多次的 node XXX.js
          但是這樣去運行 node 程序就會有一個問題,如果是一個 HTTP 服務的話,很容易就會出現(xiàn)端口沖突的問題:

          const?http?=?require('http')

          http.createServer(()?=>?{}).listen(8000)

          比如我們有這樣的一個 PM2 配置文件,那么執(zhí)行的時候你就會發(fā)現(xiàn),報錯了,提示端口沖突:

          module.exports?=?{
          ??apps:?[
          ????{
          ??????//?設置啟動實例個數(shù)
          ??????"instances":?2,
          ??????//?設置運行模式
          ??????"exec_mode":?"fork",
          ??????//?入口文件
          ??????"script":?"./test-create-server.js"
          ????}
          ??]
          }

          這是因為在 PM2 的實現(xiàn)中, fork 模式下就是簡單的通過 spawn 執(zhí)行入口文件罷了。

          實現(xiàn)位置:lib/God/ForkMode.js

          而當我們把 exec_mode 改為 cluster 之后,你會發(fā)現(xiàn)程序可以正常運行了,并不會出現(xiàn)端口占用的錯誤。
          這是因為 PM2 使用了 node 官方提供的 cluster 模塊來運行程序。

          cluster 是一個 master-slave 模型的運行方式(最近 ms 這個說法貌似變得不政治正確了。。),首先需要有一個 master 進程來負責創(chuàng)建一些工作進程,或者叫做 worker 吧。
          然后在 worker 進程中執(zhí)行 createServer 監(jiān)聽對應的端口號即可。

          const?http?=?require('http')
          const?cluster?=?require('cluster')

          if?(cluster.isMaster)?{
          ??let?limit?=?2
          ??while?(limit--)?{
          ????cluster.fork()
          ??}
          }?else?{
          ??http.createServer((req,?res)?=>?{
          ????res.write(String(process.pid))
          ????res.end()
          ??}).listen(8000)
          }

          詳情可以參考 node.js 中 TCP 模塊關(guān)于 listen 的實現(xiàn):lib/net.js
          在內(nèi)部實現(xiàn)邏輯大致為, master 進程負責監(jiān)聽端口號,并通過 round_robin 算法來進行請求的分發(fā),master 進程與 worker 進程之間會通過基于 EventEmitter 的消息進行通訊。

          具體的邏輯實現(xiàn)都在這里 lib/internal/cluster 因為是 node 的邏輯,并不是 PM2 的邏輯,所以就不太多說了。

          然后回到 PM2 關(guān)于 cluster 的實現(xiàn),其實是設置了 N 多的默認參數(shù),然后添加了一些與進程之間的 ipc 通訊邏輯,在進程啟動成功、出現(xiàn)異常等特殊情況時,進行對應的操作。
          因為前邊也提到了,PM2 是由守護進程維護管理所有的業(yè)務進程的,所以守護進程會維護與所有服務的連接。
          process 對象是繼承自 EventEmitter 的,所以我們只是監(jiān)聽了一些特定的事件,包括 uncaughtExceptionunhandledRejection 等。
          在進程重啟的實現(xiàn)方式中,就是由子進程監(jiān)聽到異常事件,向守護進程發(fā)送異常日志的信息,然后發(fā)送 disconnect 表示進程即將退出,最后觸發(fā)自身的 exit 函數(shù)終止掉進程。
          同時守護進程在接收到消息以后,也會重新創(chuàng)建新的進程,從而完成了進程自動重啟的邏輯。

          實現(xiàn)業(yè)務進程的主要邏輯在 lib/ProcessContainer 中,它是我們實際代碼執(zhí)行的載體。

          系統(tǒng)信息監(jiān)控

          系統(tǒng)信息監(jiān)控這塊,在看源碼之前以為是用什么 addon 來做的,或者是某些黑科技。
          但是真的循著源碼看下去,發(fā)現(xiàn)了就是用了 pidusage 這個包來做的- -
          只關(guān)心 unix 系統(tǒng)的話,內(nèi)部實際上就是ps \-p XXX這么一個簡單的命令。

          至于在使用 pm2 monitpm2 ls \--watch 命令時,實際上就是定時器在循環(huán)調(diào)用上述的獲取系統(tǒng)信息方法了。

          具體實現(xiàn)邏輯:getMonitorData dashboard
          list:

          https://github.com/Unitech/pm2/blob/master/lib/God/ActionMethods.js#L40

          后邊就是如何使用基于終端的 UI 庫展現(xiàn)數(shù)據(jù)的邏輯了。

          日志管理

          日志在 PM2 中的實現(xiàn)分了兩塊。
          一個是業(yè)務進程的日志、還有一個是 PM2 守護進程自身的日志。

          守護進程的日志實現(xiàn)方式是通過 hack 了 console 相關(guān) API 實現(xiàn)的,在原有的輸出邏輯基礎(chǔ)上添加了一個基于 axon 的消息傳遞,是一個 pub/sub 模型的,主要是用于 Client 獲得日志,例如 pm2 attachpm2 dashboard 等命令。
          業(yè)務進程的日志實現(xiàn)方式則是通過覆蓋了 process.stdoutprocess.stderr 對象上的方法(console API 基于它實現(xiàn)),在接收到日志以后會寫入文件,同時調(diào)用 process.send 將日志進行轉(zhuǎn)發(fā),而守護進程監(jiān)聽對應的數(shù)據(jù),也會使用上述守護進程創(chuàng)建的 socket 服務將日志數(shù)據(jù)進行轉(zhuǎn)發(fā),這樣業(yè)務進程與守護進程就有了統(tǒng)一的可以獲取的位置,通過 Client 就可以建立 socket 連接來實現(xiàn)日志的輸出了。

          hack console 的位置:lib/Utility.js hack stdout/stderr write 的位置:lib/Utility.js 創(chuàng)建文件可寫流用于子進程寫入文件:lib/Utility.js 子進程接收到輸出后寫入文件并發(fā)送消息到守護進程:lib/ProcessContainer.js 守護進程監(jiān)聽子進程消息并轉(zhuǎn)發(fā):lib/God/ClusterMode.js 守護進程將事件通過 socket 廣播:lib/Daemon.js Client 讀取并展示日志:lib/API/Extra.js

          查看日志的流程中有一個小細節(jié),就是業(yè)務日志, PM2 會先去讀取文件最后的幾行進行展示,然后才是依據(jù) socket 服務返回的數(shù)據(jù)進行刷新終端展示數(shù)據(jù)。

          后記

          PM2 比較核心的也就是這幾塊了,因為通過 Client 可以與守護進程進行交互,而守護進程與業(yè)務進程之間也存在著聯(lián)系,可以執(zhí)行一些操作。
          所以我們就可以很方便的對業(yè)務進程進行管理,剩下的邏輯基本就是基于這之上的一些輔助功能,以及還有就是 UI 展示上的邏輯處理了。

          PM2 是一個純 JavaScript 編寫的工具,在第一次看的時候還是會覺得略顯復雜,到處繞來繞去的比較暈,我推薦的一個閱讀源碼的方式是,通過找一些入口文件來下手,可以采用 調(diào)試 or 加日志的方式,一步步的來看代碼的執(zhí)行順序。
          最終就會有一個較為清晰的概念。

          ??愛心三連擊

          1.看到這里了就點個在看支持下吧,你的點贊在看是我創(chuàng)作的動力。

          2.關(guān)注公眾號程序員成長指北,回復「1」加入Node進階交流群!「在這里有好多 Node 開發(fā)者,會討論 Node 知識,互相學習」!

          3.也可添加微信【ikoala520】,一起成長。


          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人毛片18女人免费视频 | 无码视频久久 | 亚洲视频免费视频在线视频免费视频 | 天天躁日日躁狠狠躁av | 自拍偷拍欧美激情 |