升級 Node.js 版本遇到的 co 和 pm2 問題解析

背景

nodejs 4.x 的項目, 須要升級到6.9.5(當時最新的穩定版本)以改善性能和可靠性.前端

業務中使用到了co, 進程使用 pm2 管理.node

升級nodejs版本

確保構建腳本可以使用nvm安裝nodejs 6.9.5, 本地運行基本okjson

從 co 的問題開始

UnhandledPromiseRejectionWarning: Cannot read property 'done' of undefined
複製代碼

服務啓動時即產生上述報警信息, 服務不可用, 經過搜索發現是存在某個promise最終reject了, 可是沒有catch. 知道直接緣由是這樣, 但沒啥幫助, 我上哪去找這種特色的代碼.小程序

考慮斷點調試promise

process.on('unhandledRejection', function(reason, p){
  console.log('=======================');
  console.log(reason);
  console.log(p);
});
複製代碼

上述代碼加到服務開始啓動, 本地調試啓動, 發現一切正常 -_-bapp

不復現問題, 回顧整個問題, 目測多是測試環境問題, 先看看nodejs版本吧性能

console.log(process.versions)
複製代碼

居然nodejs版本仍是舊版本測試

pm2 的問題

能決定nodejs版本的途徑就只有進程啓動了, 哪問題就落到 pm2 這邊了, 去檢查pm2的進程配置spa

> pm2 show myapp_name
│ interpreter       │ node
│ interpreter args  │ --harmony
....
│ exec mode         │ cluster_mode
│ node.js version   │ 4.4.2
複製代碼

果真版本有問題, 考慮pm2這種進程管理模型, daemon進程啓動後, 再逐個啓動worker進程, 而 exec mode: cluster_mode 意味着它使用了nodejs的cluster模塊來啓動子進程.調試

進一步的, cluster啓動子進程是用fork()啓動的, 子進程的版本和父進程版本應該是一致的. 大機率是這個緣由.

簡單重啓daemon進程的辦法是 pm2 kill 幹掉daemon和全部worker進程後, 從新pm2 start. 一番折騰後的結論:

  1. daemon進程更新, 除了 pm2 kill && pm2 ping能夠重啓daemon外, 還能夠 pm2 update 它還會使用當前版本pm2
  2. 想要讓app生效仍是建議從新添加app, pm2 delete app.json && pm2 start app.json

測試環境 重啓了pm2 daemon進程後, 啓動仍舊是前文遇到的報警, 但 node.js version 輸出是符合預期了.

雖然沒解決問題, 但升級版本是必須的. 繼續看看, 收集線索

回到 co 的問題

如今nodejs版本一致, 可是測試環境報警, 本地不復現, 可能仍是環境問題, 繼續看進程配置, 發現 interpreter args: --harmony

這個是舊版本nodejs爲了兼容新的特性加的開關, 考慮到錯誤堆棧是從co中過來的, 查看co的文檔

co platform compatibility

按理v4+以後就不須要加這個開關, 暫且不關心爲何加這個開關, 目前能找到的差別就是這個地方, 先本地加上這個開關運行看看

結果復現相同的報警, 本地和測試環境現象一致

接下來就好辦了, 到app.json中刪除這段配置, pm2 delete app.json && pm2 start app.json 從新啓動app, 問題解決.

總結

  1. pm2 cluster_mode 升級nodejs時須要同步更新daemon進程
  2. worker進程的配置也須要手工更新
  3. nodejs不會保證部分實驗性開關的兼容性

實際遇到的環境問題可能都是混雜多個關鍵緣由, 必須得解決全部的緣由才能正常工做.

錯誤纔是常態, 正確是一連串的偶然組合在一塊兒

遺留疑問: --harmony 怎麼加上去的

瞭解cluster的同窗應該知道 fork() 只有一個參數 環境變量, 那就有些奇怪的地方了.

一種多是 pm2 daemon 啓動時加上去的, 但也不合邏輯, daemon可能會管理多個項目, 有的是cluster, 有的不是.

只是猜想顯然不行, 看源碼吧

God.nodeApp = function nodeApp(env_copy, cb){
  var clu = null;
 
  console.log('Starting execution sequence in -cluster mode- for app name:%s id:%s',
              env_copy.name,
              env_copy.pm_id);
 
  if (env_copy.node_args && Array.isArray(env_copy.node_args)) {
    // 注意下面這行
    cluster.settings.execArgv = env_copy.node_args;
  }
 
  env_copy._pm2_version = pkg.version;
 
  try {
    // node.js cluster clients can not receive deep-level objects or arrays in the forked process, e.g.:
    // { "args": ["foo", "bar"], "env": { "foo1": "bar1" }} will be parsed to
    // { "args": "foo, bar", "env": "[object Object]"}
    // So we passing a stringified JSON here.
    clu = cluster.fork({pm2_env: JSON.stringify(env_copy)});
  } catch(e) {
    God.logAndGenerateError(e);
    return cb(e);
  }
複製代碼

從新翻看 cluster 的文檔, 發現確實存在 cluster.settings

cluster.settings


你們好,我是貓眼娛樂前端技術專家-曹宇,我主要負責貓眼娛樂電影選座交易業務前端, 除了你們能看到的各類 Web 頁面, 還有小程序端和供應鏈端. 同時負責貓眼內部的前端基礎設施, 質量保證相關工做。

貓眼電影小程序從零發展到票務類別第一, 主要關注點都集中在線上, 此次分享的是一個線上 線下聯動的活動, 從開發到上線後遇到的一些有趣的事情, 除了小程序技術的深度應用, 還包括產品 運營層面的思考.

本週六(10月21日)我會作客掘金Bilibili直播間爲你們作一場《打碼指南:由貓眼線下掃碼1分購談起》的直播。直播中咱們也會送出技術圖書,大號定製鼠標墊等獎品,歡迎週六下午你們與咱們一塊兒交流。

相關文章
相關標籤/搜索