Node.js 源站應用穩定性保障

jopen 8年前發布 | 15K 次閱讀 Node.js 測試技術 Node.js 開發 DragonFlyBSD

源站是 CDN 技術中的一部分,是發布內容的原始站點。CDN 負責承載流量的部分稱做緩存服務器,而緩存服務器自身不生產內容,需要從源站獲取原始內容。Dragonfly 作為淘寶內容管理系統(CMS)的源站,渲染并為緩存服務器提供了所有的頁面內容。

Dragonfly 使用 Node.js 開發,穩定性保障是一邊實踐探索、一邊總結經驗。現在來回顧,穩定性保障涉及了 Dragonfly 完整的開發運維的生命周期。因此本文依次從設計、實現、驗收、運維四個環節展開。

系統設計

從設計入手,我們分別梳理了 Dragonfly 在淘寶 CMS 生態圈的拓撲圖和源站內部流程的草圖。

源站的外部環境比較簡單。前面對接 CDN,實現流量承載和核心頁面的容災,穩定性很高。后面主要對接 Redis 緩存,用于獲取頁面的各種資源。(圖中的 配置中心 和 FileSync,分別用于獲取配置和共享模板片段,為弱依賴)

TMS 支持多終端頁面的投放,因此 CDN 需要有識別終端的能力,Dragonfly 為此做了相關的處理。梳理前,源站的內部流程如下(Dragonfly 使用了 Koa.js,其中間件執行流程為回形針型——正序進,逆序出):

這里我們注意到:

  1. 沒有輸入過濾模塊,為保證環境一致,用戶 Query 需要統一丟掉。
  2. 獲取頁面入口模塊依賴 Redis 這樣欠穩定的系統,但是并沒有納入容災備份的流程。
  3. 沒有統一的異常處理模塊,容災模塊只作異常檢測,并為妥善處理異常。

為此我們先在設計上進行了如下調整:

依據新的設計,我們確認了依賴不穩定系統的模塊都能容災模塊覆蓋。同時根據這張草圖,我么確認了需要在實現、運維環節檢查的內容:

  1. 確認外部依賴系統都有容錯策略。
  2. 確認內部錯誤拋出都做了正確的日志記錄,依據自身場景作恰當的容災或其它異常處理。
  3. 確認監控腳本被正確的配置。

系統實現

依據系統設計環節的評審成果,我們開始評審系統實現部分。

外部依賴容災保障

基本原則:

  • 關鍵鏈路依賴的外部系統越少越好,外部依賴一定要有詳細的容災策略和預案。
  • 依賴也包括第三方模塊,應該使用最新穩定版本。過期版本易出現 BUG 無人解決、集成困難、性能差等問題。

Dragonfly 外部依賴的具體保障:

CDN/源站

  • CDN,根據自身特點有大量節點,依賴專業運維團隊的維護,部分節點異常不影響可用性。
  • 源站異常,CDN 使用過期的副本。

終端探測

  • 終端探測模塊使用 UA 進行判斷。遇到未知設備,可能出現判斷錯誤,因此系統提供了強制切換參數。

配置中心

  • 配置中心即 Dragonfly 使用的配置推送系統。設計上,配置中心從服務端至客戶端有多級容災。
  • 另外 Dragonfly 還在源碼中做了一份本地容災。

FileSync

  • 文件同步系統 FileSync 維護了 CMS/應用 共用的前端代碼片段。
  • 推送后存在本地,遇異常可本地容災、手工更新。

Redis

  • Redis 性能不錯,但受網絡影響,Dragonfly 實際使用時超時較多。我們做了大量測試,發現主要原因為 Redis 傳輸小數據較多,因此確保 TCP 連接做了 Keep-Alive、關閉 Nagle 算法、關閉 Delay ACK 優化后,性能得到了很大改善。
  • Redis 是數據緩存,Dragonfly 讀取數據以 Redis 為主,另外使用了 Aliyun OSS 作為備份數據源。在 Redis 異常、超時時使用 Aliyun OSS。
  • Redis、Aliyun OSS 皆異常時頁面將無法更新,源站啟用本地容災。

內部容錯保障

基本原則有:

  • 規范異常格式、拋出方式,進行統一處理是容災保障的基礎。
  • 關鍵系統資源遇到瓶頸,要有降級策略。

Dragonfly 的具體處理方式:

上下文異常處理

  • 發生后需要做日志記錄,使用靜態副本容災。

未捕獲的異常

  • 寫日志觸發報警,重啟 Worker 進程。

實時備份

  • 每個頁面請求,每 10s 生成一次靜態容災副本寫入硬盤。

內存監控

  • 渲染過程中會產生大量緩存、臨時字符串,給垃圾回收帶來了很大壓力。
  • 內存占用過高,無法及時回收時,需要強制重啟 Worker 進行回收。
  • 更多優化方案持續進行。

過載降級

  • 壓力過高時,Dragonfly 會收到 nginx 提供的 Over-Load 頭,此時直接返回靜態副本。

靜態開關

  • 與開發團隊交流學到的手動降級方案。用于應付未知 BUG 導致的大批頁面異常。

測試(驗收)

系統設計時也要一并思考如何測試。優秀的設計應該是易于測試的。

單元測試

充分的單元測試是持續重構的保障。Dragonfly 的單元測試細節本文就不展開了,這里給出筆者的一些總結:

  • 單元測試不拖累開發效率,反而是持續高效開發的保障。
  • 測試覆蓋可以驗證代碼和測試質量,幫助我們找到潛在的缺陷。
  • 單元測試設計要充分,從程序的基本單元入手,需求變更時必須及時更新。
  • 單元測試應保持獨立性,每項測試不依賴其它測試,產生可覆蓋、一致的結果。這里 Mock 是項很有用的技術。

功能測試

單元測試保障了每個模塊的質量。對于整個系統而言,功能測試是確定是否實現用戶需求的有效方法。

功能測試的實現與單元測試大同小異。要特別說明的是,對于容災模塊,除了功能測試,我們還做了線上演練。

性能測試

主要利用壓測平臺模擬真實用戶訪問,頁面使用線上機器抓取的真實地址。出現明顯性能下滑的變更不能發布。

持續集成

既然測試是質量的保障,我們應該把測試自動化。選擇一個成熟的持續集成方案吧。

日志與監控(維護)

日志

日志是用于監控和排查問題的。應以監控和問題排查者的角度記錄。做到統一格式,按模塊分類記錄,集中管理。筆者理解的日志分類為:

診斷日志

  • 例如源站會按 config/redis/xtemplate 具體分類記錄。

統計日志

  • 例如源站的 QPS/RT 等訪問日志統計。

審計日志

  • 例如用戶操作日志。

日志是需要定時維護的,這里依據 Dragonfly 的日志維護經驗給出總結:

  • 無用日志必須清理。
  • 設計日志時思考如何方便故障排查者。
  • 排查故障后,應反思日志是否合理,并及時完善日志。
  • 給一臺線上機器實現詳細 Debug 日志的開關,用于復雜問題的排查。

監控

有日志無監控等于沒做,部分監控經驗:

  • 監控信息要便于快速解決問題。
  • 監控要依據運維經驗調整合理,誤報過多容易導致麻痹。

總結

千里之堤,潰于蟻穴。系統穩定性需要持續的點滴積累,疏忽任何一個細微環節都可能給系統帶來巨大的風險。但是依賴合理的規劃、嚴格的驗收標準、持續完善的監控,穩定性保障并不困難。

來自: http://taobaofed.org/blog/2016/01/05/dragonfly-stability/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!