[譯] Node.js 之戰: 如何在生產環境中除錯錯誤

mnikn發表於2017-04-28

Node.js 之戰: 在生產環境中除錯錯誤

在這篇文章,這篇文章講述了 Netflix、RisingStack 和 nearForm 在生產環境中遇到 Node.js 錯誤的故事 - 因此你可以此為鑑,避免犯上同樣的錯誤。同時你將會學到如何除錯 Node.js 的錯誤。

感謝來自 Netflix 的 Yunong Xiao、來自 Strongloop 的 NearForm 和來自 Shubhra Kar 的 Matteo Collina 對這篇文章的見解與幫助。

過去4年裡,我們在 RisingStack 的生產環境中執行 Node 應用,積累了許多相關經驗 - 感謝 Node.js 諮詢、學習和開發 的業務支援。

Netflix 和 nearForm 的 Node 開發團隊都一樣,我們都有把除錯過程記錄下來的習慣,因此整個開發團隊 (現在是全世界的開發團隊) 都可以從我們的錯誤中學習。

Netflix 與 Node 除錯: 瞭解你的依賴庫

讓我們慢慢閱讀我們的朋友 Yunong Xiao 在 Netflix 發生的故事。

Netflix 的開發團隊發現他們的應用的響應時間在逐漸變長 - 他們部分終端的延遲每小時增加 10 ms。

同時,CPU 使用率的上升也反映了問題的存在。

[譯] Node.js 之戰: 如何在生產環境中除錯錯誤
Netflix debugging Nodejs in production with the Request latency graph

不同時間段請求的傳輸時間 - 圖片來源: Netflix

一開始,他們調查是否是 request handler 造成其響應時間變長。

在隔離測試後,他們發現 request handler 的響應時間穩定在 1 ms 左右。

所以問題並不是這個,他們開始懷疑到底層,是不是棧出現了問題。

接下來 Yunong 和 Netflix 開發團隊的嘗試是這個 CPU 火焰圖 和 Linux 效能事件

[譯] Node.js 之戰: 如何在生產環境中除錯錯誤
Flame graph of Netflix Nodejs slowdown

火焰圖反映了 Netflix 的響應速度正在變慢 - 圖片來源: Netflix

你可以從火焰圖中看到的東西是

  • 它有一些很高的棧 (這代表有許多函式被呼叫)
  • 並且一些矩形很寬 (代表我們在這些函式中耗費了一些時間)

經過深入調查,開發團隊發現 Express 的 router.handlerouter.handle.next 有許多引用。

Express.js 的原始碼揭示了一系列有趣的事情:

  • 所有終端的 Route handlers 都儲存在一個全域性陣列中。
  • Express.js 遞迴地遍歷並喚醒所有 handlers 直到它找到合適的 route handler。

在揭示謎題的解決方案前,我們需要知道更多的細節:

Netflix 的底層程式碼包含了每 6 分鐘執行的定時程式碼,從擴充資源中抓取新的路由配置資訊,更新應用的 route handlers 從而響應改變的資訊。

這些是通過刪除並新增新的 handlers 來實現的。意外的是,同時它再一次新增了相同的靜態 handler - 甚至是以前的 API route handlers。這造成的結果是,響應時間額外增加了 10 ms。

從 Netflix 的錯誤中獲取的教訓

  • 一定要了解你的依賴庫 - 首先,你必須在生產環境中使用它們之前,徹底地瞭解它們。
  • 可觀察性是關鍵 - 火焰圖幫助 Netflix 工程團隊解決了問題。

從這裡閱讀整個故事: 火焰圖中的 Node.js

當你最需要幫助時候的專家指引

商業化 Node.js,由 RisingStack 提供

瞭解更多

RisingStack CTO: "加密是要花時間的"

你可能已經聽過我們的故事 拆分單體式應用的故事,我們的 CTO Peter Marton 把 Trace (我們的 Node.js 監控系統) 分離成多個微服務模組。

我們現在討論的錯誤是 Trace 開發時的響應速度變慢:

作為一個在 PaaS 執行的 早期 Trace 版本,它通過公共雲來與我們的其他服務通訊。

為了確保我們的請求是完整的,我們決定對所有請求進行簽名。為了實現這個,我們看了 Joyent 的 HTTP signing library。很棒的是,request 這一模組支援開箱即用的HTTP簽名。

解決方案代價不僅很大,而且會對我們的響應速度造成不好的影響。

[譯] Node.js 之戰: 如何在生產環境中除錯錯誤
network delay in nodejs request visualized by trace

網路延遲增加了我們的響應時間 - 圖片來源: Trace

從圖中可看到,所給定的終端響應速度為 180 ms,然而對於總體來說,單獨兩個服務的網路延遲只是 100 ms

一開始,我們 用 Kubernetes 轉移 PaaS provider。我們希望響應速度會快一點,這樣內部網路就會平衡。

我們的方法奏效了 - 終端的響應速度提高了。

然而,我們想要更好的結果 - 大幅度降低 CPU 的使用率。下一步是分析 CPU 的使用情況,就像 Netflix 的人們做的一樣:

[譯] Node.js 之戰: 如何在生產環境中除錯錯誤
crypto sign function taking up cpu time

從截圖可以看出,crypto.sign 函式消耗的 CPU 時間最多,每次請求花費 10 ms。為了解決這個問題,你有兩種選擇:

  • 如果你在可信任的環境中執行應用,你可以去除請求籤名,
  • 如果你在不可信的環境中執行,你可以升級你的機器讓它擁有更強大的 CPU。

從 Peter Marton 中獲取的教訓

  • 服務之間的終端資訊傳輸會對使用者體驗有巨大的影響 - 儘可能的平衡內部網路。
  • 加密可能會消耗大量時間

nearForm: 不要堵塞 Node.js 的事件迴圈

React 現在很流行。開發者在前端和後端都會使用它,甚至他們更進一步用它來構建同構的 JavaScript 應用。

然而,渲染 React 頁面會讓 CPU 有挺大的負擔,當繪製複雜的 React 內容時會受到 CPU 限制。

當你的 Node.js 正在進行繪製,它會堵塞事件迴圈,因為它的行為都是基於同步的。

結果就是,伺服器可能會毫無反應 - 當請求堆積起來,會把所有的負擔都堆在 CPU 上。

更糟的是即使請求端已經關閉,請求仍然會被處理 - 仍然會對 Node.js 應用造成負擔,nearForm 對此有解釋 Matteo Collina

不僅是 React,大多數字符串操作也會這樣。 如果你在構建 JSON REST APIs,你應該花心思在 JSON.parseJSON.stringify

Strongloop(現在是 Joyent) 的 Shubhra Kar 對此解釋是,解析和轉化成 JSON 字串的等消耗巨大的操作也會消耗大量時間 (同時在這期間會堵塞事件迴圈)

functionrequestHandler(req, res) {  
  const body = req.rawBody
  let parsedBody
  try {
    parsedBody = JSON.parse(body)
  }
  catch(e) {
     res.end(newError('Error parsing the body'))
  }
  res.end('Record successfully received')
}複製程式碼

簡易的 request handler

這個例子展示了一個簡易的 request handler,用來解析 body。對於內容不多的情況下,它執行的挺好 - 然而,如果 JSON 的大小要以兆來描述的話,可能會花費數秒的時間來執行 而不是在毫秒時間內執行。同理 JSON.stringify 也一樣。

為了緩解這個問題,首先你要了解它們。為此,你可以用 Matteo 的 loopbench 模組,或者 Trace 的事件迴圈度量功能。

通過 loopbench,如果請求沒有被實現,你可以返回狀態碼 503 給負載平衡器。為了啟用這項功能,你要使用選項 instance.overLimit。這樣 ELB 或者 NGINX 可以在不同的後端中重試,這樣請求有可能會被處理。

一旦你瞭解這個問題並理解它,你就能開始修正它 - 你可以通過平衡 Node.js 流或者改變正在使用的架構來進行修正。

從 nearForm 中獲取的教訓

  • 總要留心對 CPU 負擔大的操作 - 這類的操作越多,在你的事件迴圈裡對 CPU 造成的壓力越大。
  • 字串操作會對 CPU 造成巨大負擔

在生產環境中除錯 Node.js 錯誤

我希望 Netflix、RisingStack 和 nearForm 的例子會對你在生產環境中除錯 Node.js 應用有幫助。

如果你想要了解更多,我建議看下最近這些文章,它們會加深你的 Node 知識:

如有任何疑問,請留下評論讓我們知道!


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章