記一次 Node debug 過程

hello~親愛的看官老爺們你們好~最近接手維護公司另外一個 Node 項目,稍微熟悉一下代碼後,便被提了一個解決線上 bug 的需求。定位問題後解決仍是十分容易的,可是這個過程十分有趣,bug 出現的緣由也值得深思。於是有了這篇文章,分享此次 bug fixes 的過程。ios

背景

項目是搜索相關的,頁端發請求到 Node 端,Node 包裝搜索請求參數後轉發 JavaJava 返回數據後,Node 將數據組裝成 HTML 文檔返回給頁端。整個過程很是常規,但 Bug 出現的位置就十分奇妙了,線上偶現渲染出來的 HTML 文檔中部分 a 標籤連接指向了錯誤但有意義的地址。簡單的代碼以下:axios

const axios = require('axios');
const _arr = [
  //數組內是一個個對象,保存着一些公用的數據
];

router.get('/test', (req, res) => {
  axios(
    //請求 Java 的各類配置
  )
    .then(response => {
      //對 response 進行處理,省略若干步驟
      for (let item of _arr) {
        item.url = `?${querystring.stringify(response.url)}`;
      }
    })
    .then(() => {
      //其餘的異步操做
    })
    .then(data => {
      res.render({
        //...各類數據
        _arr
      })
    })

})

module.exports = router;
複製代碼

若是對 Node 模塊機制比較熟悉的同窗,應該能看出問題的根源了。但若是是接手的項目,並且實際代碼比這個複雜 N 倍(項目中此接口大約有700行代碼),估計就沒那麼好分析了。數組

問題分析

當時我接手時,在本地環境和測試環境,沒法復現出相同的問題。同時,被修改的連接實際上是從新搜索的連接,如以前搜索的是 123,跳轉的連接正常來講仍是 123 的,但會變爲 xxx風景區。於是一度認爲是有中間人進行攻擊,修改了模板中的連接。但分析的過程當中,發現兩個不合理的地方:閉包

  • 若是真的是中間人攻擊,那麼爲什麼不修改所有的連接?
  • 搜索頁面支持 Https,修改修改協議後,線上環境仍偶現該問題。

若是說第一點還情有可原的話,第二點基本就認定不是中間人的問題,除非中間人強到破解了加密或公司的私鑰泄密了。因而問題又回到了原點,代碼到底哪裏又出問題了呢?框架

線上環境是較難調試的,但能夠經過打日誌的方式進行分析。經過打點,發現請求 Java 後返回的 response.url 是正確的,但在渲染時 _arrurl 卻不一樣了,能夠肯定問題是出在 _arr 上,由於某些緣由,它的值被修改了。異步

定位到具體出現問題的地方後,緣由歸結起來就很簡單了,Node 中的模塊機制,實際上是經過函數包裝而成的,在此過程當中會造成閉包,簡略示例以下:函數

const module = {}; //全局變量

function(exports, require, module, __fileName, __dirname) {
  const _arr = [];
  module.exports = ....;
}
複製代碼

每一個文件都會被加工成上面代碼的形式,而 module 是全局可訪問的。並且須要記住一點,不是每次用到某個文件時,函數都會執行一次,而是隻要該文件被 require 一次後,它的相關代碼已經被記錄在全局的 module 中了。於是 _arr 是以單例的形式存在的,若是 exports 出去的代碼對 _arr 作了修改的話,會影響此後對 _arr 的讀取。好像有點繞,再簡單一點說,如一下 demo測試

function wrap() {
  const arr = [];
  return function() {
    console.log(arr);
    arr.push(1);
  }
}

const module = wrap();

module(); //[]
module(); //[1]
module(); //[1, 1]
複製代碼

而項目的代碼有這麼一段:ui

for (let item of _arr) {
    item.url = `?${querystring.stringify(response.url)}`;
}
複製代碼

這段代碼是有反作用的,它修改了 _arr 的內容,若是以後都是同步的代碼,那麼問題可能還不大(其實仍是有問題,但不會影響結果),但只要是異步的,那麼問題可能就會出現。知道了問題的根源,解決就很簡單了,要麼每次調用都從新定義 _arr 一次,要麼將代碼改成無反作用的~加密

小結

以上就是整個 debug 過程的簡要描述,其實只是以前開發的同窗某些細節沒有考慮到,還好訪問量不大,但形成的後果不算特別嚴重。然而分析下來,發現這個 bug 是若干 Js 基礎問題共同做用而成的,當咱們不斷追求新技術、新框架的同時,是否是該靜下心來,好好鞏固一下語言的基礎呢?

感謝各位看官大人看到這裏,知易行難,但願本文對你有所幫助~謝謝!

相關文章
相關標籤/搜索