动态

详情 返回 返回

在企業環境中正確使用 Node.js 的九大原則 - 动态 详情

鏈接:https://www.platformatichq.com/node-principles

作者:James Snell 等

原標題:9 Principles for Doing Node.js Right in Enterprise Environments

Node.js 為超過 630 萬個網站和無數的 API 提供支持,是包括沃爾瑪和網飛在內的現代應用程序的有效基石。每年有超過 20 億次的下載量,它是當今最常用的 Web 開發工具之一(OpenJS 基金會)。對於構建高性能應用程序來説,它是一個絕佳的選擇,然而,從經驗中我們知道,管理、操作和擴展一個應用程序需要大量的專業知識。

多年來,Platformatic 的聯合創始人盧卡·馬拉斯基(Luca Maraschi)和馬泰奧·科利納(Matteo Collina)與財富 500 強公司的數十個工程團隊合作,設計了他們最重要的 Node.js 應用程序。作為 Node.js 技術指導委員會的一員,馬泰奧非常榮幸能夠近距離接觸 Node 社區及其不斷變化的需求。如果你以前構建過 Node.js 應用程序,那麼你可能運行過他的代碼。他維護的模塊每月被下載 26 億次,這使得他獨自承擔了大約所有 npm 流量的百分之一。

為了分享我們的見解,我們創建了“九節點支柱”——在企業環境中創建強大、可擴展且可維護的 Node 應用程序的九條指導原則。本指南可用作檢查清單,以確定當前實踐中的差距,並確定需要改進的優先領域。

為了創建儘可能全面的指南,我們希望利用 Node.js 社區的集體智慧。話雖如此,感謝我們出色的貢獻者,他們抽出時間與我們一起完成這一作品。他們的見解和經驗在完善“九大 Node 支柱”方面非常寶貴。

1. 不要阻塞事件循環

Node.js 的事件驅動架構是其性能和可擴展性的基石。這個架構的核心是事件循環,一種處理異步操作並確保應用程序保持響應的機制。

事件循環是使 Node.js 能夠執行非阻塞 I/O 操作的機制——儘管默認情況下只使用單個 JavaScript 線程——通過在可能的情況下將操作卸載到系統內核。

事件循環由以下階段組成,每個階段負責處理不同類型的事件:

img

事件循環在一個連續的循環中遍歷這些階段。如果在特定階段沒有事件要處理,循環將移動到下一個階段。這個過程會持續進行,直到沒有更多的事件要處理。

阻塞事件循環

事件循環以循環的方式運行,不斷檢查新事件並執行相應的回調。當發生阻塞操作時,事件循環在操作完成之前無法處理其他事件,從而導致待處理任務積壓。低效的代碼可能會無意中阻塞事件循環,從而導致性能下降和潛在的應用程序不穩定。

阻塞事件循環的後果:

  • 性能降低:由於事件循環停滯,應用程序處理併發請求的能力顯著下降,從而影響響應時間。
  • 增加的延遲:當事件循環被阻塞時,隨着請求排隊,用户會遇到延遲。
  • 無響應:在極端情況下,應用程序可能會出現凍結或變得無響應。

最佳實踐:

為防止事件循環阻塞並保持最佳性能:

  • 分解複雜任務:將複雜任務分解為較小的異步步驟,以防止長時間運行的操作阻塞事件循環。這可以涉及有效地使用 Promise、async/await 或回調函數。
  • 卸載 CPU 密集型任務:對於計算量大的操作,考慮使用工作線程將它們與主事件循環隔離開來。即使你的進程只處理一種類型的請求並在請求到達時按順序處理它們,這一點也很重要。例如,在 Kubernetes 下運行時,如果事件循環因長時間運行的請求而被阻塞,活性檢查可能會失敗。

像piscina這樣的庫可以簡化將 CPU 密集型任務卸載到工作線程的過程。

-Matteo Collina

  • 實現緩存和去重:通常在每次 I/O 操作(如數據庫或 API 調用)之後都有一個受 CPU 限制的操作。通過在處理後緩存結果來減少它或減少 API 調用;嘗試對它們進行去重。像 async-cache-dedupe 這樣的庫可以對此提供幫助。
  • 監控事件循環利用率:使用諸如“perf_hooks”之類的工具或“under-pressure”插件來跟蹤事件循環性能並識別潛在的瓶頸。
  • 理解某些操作發生的位置:瞭解 Node.js 中某些操作發生的位置對於性能至關重要。例如,JSON 解析在主線程上進行,而異步文件讀取則在單獨的線程上進行。這種區分有助於你優化代碼以避免阻塞事件循環。

2. 監控Node特定指標並對其採取行動

標準的 Node 應用指標通常缺乏有效故障排除所需的上下文。因此,監控 Node.js 性能通常需要結合來自多個工具和自定義儀表板的見解,同時兼顧 CPU 使用率、內存消耗和延遲等指標。

這種碎片化的視圖使得難以確定速度減慢和應用程序無響應的根本原因。

想象一個擁有眾多微服務的平台;片面的視圖可能會顯示 CPU 使用率很高,但在沒有上下文的情況下確定罪魁禍首仍然是一場猜謎遊戲。這阻礙了及時的故障排除,導致持續的性能瓶頸,使公司損失數百萬的收入。

那麼,你應該監測什麼呢?

  • 內存使用情況:

    • 使用的堆與堆總量:跟蹤正在使用的堆內存比例,以識別潛在的內存泄漏或低效的數據結構。
    • RSS(Resident Set Size):衡量進程所佔用的物理內存總量。

  • CPU 使用率:監控 CPU 利用率,但將其與 ELU 等其他指標關聯,以避免過早做出擴展決策。

  • 事件循環利用率 (ELU):此指標衡量 Node.js 事件循環主動處理事件的時間佔總耗時的比例。它量化了事件循環的繁忙程度。


根據指標採取行動
有兩個關鍵點可以確保你根據指標採取符合應用程序最佳利益的行動:


  1. 不要在內存使用量有限的情況下擴展或終止實例。Node.js 將使用提供給進程的所有內存。因此,當進程的內存使用量達到 60-80% 時,終止或擴展進程是一種資源浪費。
  2. 在根據 CPU 指標進行擴展時,請務必考慮 ELU。Node.js 消耗了 100% 的可用 CPU 並不意味着該進程沒有響應。

在生產中制定應急計劃

即使採用最佳實踐,也會出現意外問題。

精心設計的應急計劃對於最大限度地減少停機時間並確保在發生事故時順利恢復至關重要。作為應急計劃的基礎,你應該擁有特定於節點的監控系統,以便儘早發現問題並設置警報以通知相關團隊。

在此之後,你的計劃應包括:


  • 自動回滾和金絲雀部署:配置你的部署管道,以便在出現問題時自動回滾到以前的穩定版本。在全面推出之前,逐步向部分用户推出新版本,以識別和解決潛在問題。
  • 容器化和編排:將你的應用程序及其依賴項打包到可移植容器中。Kubernetes 等工具可以簡化容器的管理和擴展,從而更容易從故障中恢復。
  • 事件響應程序:建立響應事件的明確指南,包括角色和職責、溝通協議和升級路徑。

3. 在生產中使用 Node LTS(長期支持)版本

Node.js 長期支持版(Long-Term Support)的發佈系列會在較長時間內得到專門維護,為生產環境提供穩定性和可預測性。長期支持版系列會在三年內獲得關鍵錯誤修復和安全補丁,而非長期支持版系列只有七個月。

使用長期支持(LTS)發佈線的好處:

  • 長期支持版本降低了重大變更的風險:長期支持版本優先考慮與現有軟件包和模塊的兼容性,最大限度地減少意外中斷的可能性。
  • 增強安全性:通過使用長期支持版本,你可以受益於及時的安全更新,保護你的應用程序免受潛在威脅。
  • 提高穩定性:長期支持(LTS)版本經過嚴格測試和維護,確保更可預測和可靠的運行時環境。

LTS 版本的工作原理

Node.js 為 LTS 版本制定了嚴格的發佈時間表,每個版本都會在規定的時間內獲得主動支持,然後再獲得維護支持一段時間。這種結構化方法可確保你有足夠的時間將應用程序更新到較新的 LTS 版本,而不會影響穩定性。


img

使用 LTS 版本的最佳實踐

  • 監控 LTS 發佈時間表:隨時瞭解即將發佈的 LTS 版本並相應地規劃你的遷移策略。
  • 徹底測試:在遷移到新的 LTS 版本之前,進行徹底的測試以確保與你的應用程序和依賴項的兼容性。

  • 利用依賴管理工具:使用 npm audit 或 yarn audit 等工具來識別和解決依賴項中的漏洞,並確保它們與 LTS 版本兼容。


使用廢棄版本的危險

如圖所示,Node.js v14 和 v16 分別已停用 2 年和 1 年,但它們仍然很受歡迎。這凸顯了我們看到的問題——公司沒有更新他們的 Node.js 運行時。

img


4. 儘可能實現測試、代碼審查和一致性的自動化

編寫自動化測試
自動化測試不僅對於構建可靠且可維護的 Node.js 應用程序至關重要,而且對於加速開發也至關重要。通過建立全面的測試策略,組織可以自信地進行更改,降低缺陷風險,並最終加快步伐。

“測試最佳實踐。”

  • 定義測試用例:一旦有了測試用例,開發人員就可以開始編寫測試。這些測試可能需要針對單個測試或大規模環境的特定配置或固定裝置,包括數據和網絡基礎設施。
  • 優先進行現場測試:只要可行,就與實時系統進行交互,而不是模擬組件,以發現潛在的集成問題。
  • 專注於行為:測試應主要驗證代碼的外部行為,將組件視為黑盒。
  • 消除測試的不穩定性:持續調查並解決測試失敗問題以保持測試的可靠性。保持持續集成處於綠色狀態。
  • 避免全局狀態:減少全局狀態可降低測試的不穩定性,從而通過隔離提高測試的穩定性。
  • 專注於深度防禦性測試:雖然代碼覆蓋率是一個有價值的指標,但專注於深度防禦性測試對於確保 Node.js 應用程序的可靠性至關重要。這涉及到測試邊界情況、負面測試和安全測試。
  • “測試儘可能接近生產環境:為了準確評估應用程序在實際條件下的行為,儘可能接近生產環境進行測試至關重要。為此,設置暫存環境,並進行性能測試和集成測試。”

全面的測試覆蓋範圍

全面的測試策略涵蓋多種測試類型:


  • 單元測試用於單獨驗證各個代碼單元的正確行為。

  • 集成測試用於驗證不同組件如何交互。

  • 端到端測試模擬真實的用户場景,以確保應用程序按預期運行。


通常,我們將數據庫視為單元的一部分。因此,我們不建議模擬數據庫驅動程序,而是直接訪問數據庫,至少對於“快樂路徑”而言是這樣。模擬數據庫驅動程序對於測試錯誤情況非常有用。

選擇正確的測試框架

正確選擇測試框架至關重要。雖然 Jest 越來越受歡迎,但它在全局狀態管理和錯誤處理方面的侷限性使其不太適合 Node.js 測試。具體來説,Jest會覆蓋 Node.js 的全局變量,從而在處理網絡和數據庫時導致各種問題。如果你有一個大型 Jest 測試套件,我們建議切換到jest-light-runner,這樣可以避免所有全局變量的 monkey-patching。

考慮Vitest、Node-tap、jest-light-runner、import('node:test' )、tape或Mocha等替代方案,它們可以更好地支持 Node.js 特定的測試要求。

我們建議使用 Playwright 進行涉及瀏覽器的 E2E 測試。

TypeScript 和測試

TypeScript 雖然提供了有價值的靜態類型檢查,但並不能消除進行全面測試的需要。測試對於確保運行時正確性和捕獲 TypeScript 類型系統可能遺漏的潛在問題至關重要。

如果你使用單獨的類型編寫 JavaScript,請不要忘記對它們進行測試 - 我們建議使用 tsd 來測試類型。

Mocking

模擬涉及用模擬版本替換真實依賴項以隔離組件並簡化測試。當你希望在相同條件下始終使測試可重現時,它也很有用。一個很好的例子是從你無法控制的遠程服務器下載外部資源的組件。

常見的mock庫包括:


  • Node: test
  • sinon
  • proxyqurie
  • Esmock

一些測試框架,如 Jest 或 node:test,提供集成的模擬功能。

代碼質量和安全

確保 Node.js 應用程序的質量和安全性是交付可靠且值得信賴的軟件的關鍵。通過採用強大的測試實踐、利用靜態分析工具並定期進行漏洞掃描,你可以顯著增強代碼庫的整體健康狀況。

關鍵實踐:

  • 定期漏洞掃描:使用 Snyk、SonarQube或npm audit等工具主動識別並解決潛在的安全威脅。
  • 靜態分析和 linting:使用 ESLint等工具在開發早期檢測語法錯誤、代碼樣式違規和潛在的安全漏洞。

通過參與這些實踐,你將受益於:


  • 提高代碼質量:增強可讀性、可維護性和對最佳實踐的遵守。

  • 降低安全風險:主動識別和減輕漏洞。

  • 更快的開發:及早發現缺陷可減少生產過程中的缺陷數量和所需的返工量,有效提高團隊的速度。

5. 避免依賴蔓延

依賴關係蔓延會嚴重影響 Node.js 應用程序的複雜性、可維護性和性能。瞭解與過度依賴相關的風險並實施有效的管理策略對於構建強大而高效的軟件至關重要。

依賴關係樹
你引入項目的每個新包都可能在後續引入其他依賴關係。這可能會導致複雜的依賴關係樹,從而難以管理和了解與每個包相關的潛在風險。

避免依賴關係蔓延的關鍵注意事項:

  • 意向性:有意引入依賴關係,仔細評估其好處和潛在風險。
  • 質量和可靠性:優先考慮經過充分測試、維護良好且擁有良好維護記錄的軟件包。
  • 大小和範圍:選擇更小、更原子的包,專注於特定功能,減少引入不必要依賴的可能性。

你可以使用以下策略有效地管理依賴關係並避免不必要的膨脹。

默認使用原生 Node API,並瞭解它們與 Web 標準 API 的區別

要構建高性能且可維護的 Node.js 應用程序,使用底層運行時至關重要。通過優先使用原生 Node.js API,開發人員可以降低不必要的依賴項帶來的風險並符合既定標準。

利用原生功能:

  • 核心模塊:儘可能 利用 Node.js 提供的內置模塊(例如fs、 http 、path)。這些模塊經過高度優化、久經考驗,併為常見操作提供了熟悉的界面。
  • API 採用: 在新的 Node.js API 穩定後,採用它們。例如,使用fetch() API 可以替代傳統的http.request API ,這是一種更現代的替代方案。通過隨時瞭解最新進展,你可以從性能改進和增強功能中受益。

原生 API 具有多種優勢。它們針對 Node.js 進行了精心優化,與第三方庫相比,性能更快。此外,核心模塊經過 Node.js 核心團隊的廣泛測試和維護,確保穩定性並降低意外問題的風險。最後,由於原生 API 的熟悉性和一致性,嚴重依賴原生 API 的代碼庫往往更容易理解和維護。

瞭解原生 Node.js API 與 Web 標準 API

雖然許多 Node.js API 都符合 Web 標準,但仍然存在明顯差異,尤其是在較新的 Node.js 版本中。以下是一些主要區別:

  • Streams:與 Web 標準流相比,Node.js 流提供了對數據流更精細的控制。Node.js 流可以暫停、恢復並直接傳輸到其他流,為各種用例提供靈活性。

  • HTTP: Node.js 的 http 模塊提供對 HTTP 請求和響應的低級控制,允許進行細粒度的自定義。像 fetch 這樣的 Web 標準 API 為更簡單的 HTTP 交互提供了更高級別的抽象。然而,Node.js 的最佳 HTTP 客户端是undici,它是為了解決 http 模塊的所有遺留問題而構建的。
  • 文件系統: Node.js 的 fs 模塊提供了一組豐富的 API 用於處理文件和目錄,提供比 Web 標準文件 API 更精細的控制。

通過了解這些差異,你可以做出明智的決定,何時使用原生 Node.js API 以及何時利用 Web 標準 API 來滿足你的特定需求。

儘可能重用模塊

Node_modules是宇宙中最重的物體,這是有原因的。

img

Node.js 和 NPM 共同解決了所有以前的包管理器的關鍵問題:它們允許在同一進程中加載同一庫的多個版本。這引發了註冊表中模塊的激增,使開發人員可以重用代碼而不必擔心 API 衝突。這種模塊重用模式大大減少了工作量,並幫助組織更快地進入市場。它還有助於採用行業標準模式,而不是重新發明維護成本更高的輪子。NPM

NPM 概覽

NPM是 Node 包管理器,它支持高效且可擴展的 Node.js 開發。NPM

的核心設計旨在促進大規模代碼重用。通過為可重用的代碼包提供集中存儲庫,NPM 使開發人員能夠共享和利用常用功能,從而加快開發週期並減少冗餘。


利用 NPM 進行代碼重用的主要好處包括:

  • 加速開發:通過重用現有模塊,開發人員可以專注於構建新功能,而不是重新發明輪子。

  • 提高代碼質量:共享模塊經過更嚴格的測試和改進,從而提高整個組織的代碼質量。

  • 增強的可維護性:集中式代碼管理簡化了更新和錯誤修復。

  • 增強協作: NPM 促進團隊之間的知識共享和協作。特別是,通過使用私有註冊中心並與標準機構、開源社區和多元化的開發人員羣體合作,組織可以從共享知識、代碼重用和開源生態系統的整體實力中受益。

全面採用 Node.js 的組織也應該採用 NPM 作為促進內部代碼共享的工具。通過創建和發佈私有 NPM 包,團隊可以在公司內部有效地共享可重用模塊,從而避免重複工作並確保項目一致性。這種方法培養了協作和知識共享的文化,提高了開發人員的生產力並提高了代碼質量。


為了充分利用 NPM,請確保:


  • 標準化依賴關係:在你的組織內建立一組通用的依賴關係,以減少冗餘並簡化維護。

  • 在內部共享代碼:考慮創建和發佈私有 NPM 包,以便在組織內共享可重複使用的模塊。這可以促進協作、避免重複工作並確保項目間的一致性。

Monorepos:擴展代碼重用
對於具有多個互連組件的大型項目,monorepos 可以改變遊戲規則,提供高效的依賴管理、原子提交、簡化的代碼共享和改進的開發人員體驗。

然而,成功採用需要仔細的考慮和規劃。

主要考慮因素:

  • 治理:實施單一倉庫需要明確的治理指南來管理依賴關係、確保代碼質量並維護整個項目的一致性。
  • 依賴管理:有效的依賴管理策略對於防止衝突和確保模塊之間的兼容性至關重要。這可能涉及版本控制策略、依賴鎖定和自動化測試。

  • 工具:選擇合適的工具來簡化 monorepo 管理。這些工具可以自動執行跨多個軟件包的版本控制、發佈和測試等任務。


雖然monorepos的優勢是巨大的,但必須權衡它們與潛在的缺點:

  • 複雜性增加:管理包含多個包的單個存儲庫比管理單獨的存儲庫更為複雜。

  • 性能開銷:構建和測試整個項目可能非常耗時,尤其是對於大型代碼庫而言。

  • 衝突的可能性: 一個包中的更改可能會無意中影響其他包,從而增加調試工作量。


那麼,你應該使用哪個註冊客户端?

有多種選擇:

  • pnpm
  • npm
  • yarn

我個人的建議是,對於簡單的模塊使用 npm,對於 monorepo 場景使用 pnpm。

-馬特奧·科利納


6. 降低依賴項的風險

消除依賴關係的風險對於安全性、性能和可維護性至關重要。通過定期掃描漏洞、及時更新和選擇維護良好的依賴關係,你可以降低與依賴關係管理相關的風險,並確保你的應用程序保持安全、高效和適應性。

掃描依賴項以查找常見漏洞和暴露 (CVE)

優化 Node.js 應用程序的安全性和性能至關重要。
此安全態勢的基石是定期進行依賴項掃描,以便在開發生命週期的早期識別和解決漏洞。忽視這一做法可能會導致災難性的後果。

什麼是 CVE?

CVE 是一份已分配 CVE ID號的公開披露的計算機安全漏洞列表。CVE系統於 1999 年推出,由MITRE 公司運營。漏洞必須滿足以下條件才能被分配 CVE ID:

  1. 可獨立固定
  2. 受影響的供應商已確認或已記錄此情況
  3. 它影響一個代碼庫

掃描最佳實踐:

  • 作為 CI/CD 管道的一部分執行自動掃描,以自動檢查新舊依賴項中的漏洞。

  • 實施漏洞管理流程,根據嚴重程度和潛在影響確定補救措施的優先順序。

使用npm audit、Socket.dev或Snyk等工具來簡化漏洞管理流程。

-馬特奧·科利納

通過主動解決漏洞,組織可以顯著降低安全漏洞的風險並保護其應用程序免受惡意攻擊。

請記住,選擇正確的依賴項對於構建安全且可維護的應用程序至關重要。在將依賴項集成到你的項目之前,請仔細評估依賴項。

為了減輕供應鏈帶來的風險,你需要優先考慮能夠有效更新依賴項的依賴項管理工具,並遵循依賴關係圖進行正確的排序和加權。這有助於最大限度地降低週期性依賴項的風險以及可能降低應用程序性能或導致崩潰的其他問題。

保持依賴項最新

除了安全考慮之外,優化依賴項的使用也至關重要,以避免性能瓶頸並改善整體應用程序運行狀況。

關鍵做法:

  • 依賴最小化:減少依賴項的數量以最大限度地減少潛在衝突並縮短構建時間。
  • 依賴項版本控制:仔細管理依賴項版本以確保兼容性並避免重大更改。

  • 依賴樹優化:分析依賴樹以識別潛在的性能影響並優化包安裝。

  • 依賴項更新:定期審查和更新依賴項,以便從錯誤修復、性能改進和新功能中受益。


忽視依賴管理的危險
未能維持最新的依賴關係將導致重大挑戰。

Node.js 的長期支持 (LTS) 發佈模型強調穩定性,但必須認識到這些版本不會自動更新。依賴過時的依賴項會使你的應用程序面臨安全漏洞、性能下降和兼容性問題。

此外,當庫達到使用壽命時,你可能會遇到嚴重錯誤或錯過重要功能。這可能會使你的整個自動化管道變得毫無用處,阻礙開發工作,增加維護成本,並可能危及整個應用程序。

主動依賴項管理對於減輕這些風險並確保你的 Node.js 項目的長期健康至關重要。

通過支持關鍵項目和依賴項來管理依賴風險

開源生態系統是 Node.js 開發的基石。無法支持關鍵項目和依賴項可能會帶來重大的業務風險,包括漏洞暴露、性能下降、功能限制、供應商鎖定和社區分裂。為了培育一個可持續發展和蓬勃發展的社區,組織必須積極貢獻和支持關鍵項目和依賴項。

回饋社區

  • 開源貢獻:通過提交錯誤報告、代碼改進或新功能來參與開源項目。你的貢獻有助於提高整個社區使用的工具的質量和可靠性。

  • 資金支持:考慮贊助對你的開發工作至關重要的開源項目。這種支持可以幫助維護者分配更多時間和資源來開發和改進項目。

  • 依賴管理:積極管理項目的依賴。保持它們與最新版本同步,及時報告問題,並在可能的情況下幫助修復錯誤或增強功能。


支持 OpenJS 基金會
是一個致力於促進 JavaScript 和相關技術發展的非營利組織。

通過贊助 OpenJS 基金會,你可以直接支持關鍵 Node.js 項目和計劃的開發和維護。


你的支持還有助於應用程序所依賴的依賴項的長期健康和質量。此外,積極與項目維護者互動可以更快地解決問題並縮短響應時間。最後,為開源項目做出貢獻可以促進一個強大的協作社區,讓所有參與者受益。


7. 避免使用全局變量、配置或單例

在企業開發環境中,構建結構良好且可維護的應用程序需要有意識地儘量減少全局變量、配置和單例模式的使用。

使用依賴注入模式來構建代碼

在企業環境中,儘量減少使用全局變量至關重要。

全局變量會引入緊密耦合,使得代碼更難進行測試、推理和重構。通過採用依賴注入並促進模塊化設計,開發人員可以創建更加獨立且可複用的代碼組件。

依賴注入涉及將依賴項明確地作為函數參數或構造函數參數傳遞,而不是依賴隱式的全局訪問。這種方法增強了代碼的可測試性,促進了更好的代碼組織,並降低了意外副作用的風險。為了有效地實施這一原則,組織應建立編碼標準,阻止全局變量的使用,併為開發人員提供關於依賴注入和模塊化設計的培訓和資源。

企業環境需要關注高效的模塊管理。一個關鍵原則是最大限度地實現代碼複用——這種做法對長期開發和維護具有重大益處。

單例模式的缺陷

一個常見的錯誤是使用模塊單例來保存狀態變量。這會在模塊之間創建緊密耦合,使得重構和更新具有挑戰性。單例還可能在應用程序的不同部分依賴於同一模塊的不同實例時導致版本衝突。

例如,考慮這樣一種情況,多個模塊導入一個包含隨機數生成器的共享模塊,這個隨機數生成器被實現為單例模式。雖然一開始很方便,但如果一個模塊使用了過時的版本,那麼在共享模塊中更新隨機數生成邏輯可能會導致意外行為。

依賴注入來拯救

依賴注入通過促進模塊之間的鬆散耦合提供了一種解決方案。不是模塊直接創建或需要它們的依賴項,而是在模塊實例化或調用時將依賴項作為參數傳遞。這允許:

  1. 提高靈活性:模塊變得獨立,從而更容易進行測試、交換實現和模擬行為。

  2. 提高可維護性:只要依賴接口保持一致,對一個模塊的更改對其他模塊的影響就會很小。


示例:使用依賴注入解耦

假設一個模塊負責檢索訪問令牌,另一個模塊執行需要令牌的操作。使用依賴注入,可以解耦令牌檢索邏輯:


  • 訪問令牌檢索模塊公開一個函數(getToken)來檢索令牌。

  • 操作模塊在創建過程中接收getToken函數作為依賴。


這種方法允許獨立測試和更新每個模塊而不會影響其他模塊。

一個模塊一個功能原則

主要優點:

  • 隔離性和可重用性:模塊變得獨立,從而更容易進行測試、交換實現和模擬行為。

  • 可擴展性:模塊可以獨立開發和部署,從而更容易擴展和維護。

  • 微服務基礎:基於模塊的設計為必要時過渡到微服務架構奠定了基礎。

注重高內聚、鬆耦合

要創建模塊化、可測試和可維護的 Node.js 應用程序,請遵循高內聚和鬆散耦合的原則。


  • 高內聚性:模塊應具有單一、明確定義的職責,並專注於相關功能。這使模塊更易於理解、測試和修改。

  • 鬆散耦合:模塊對其他模塊的依賴應該最小,以降低出現意外副作用的風險並提高可維護性。


為了做到這一點:

  • 明確職責:每個模塊都應該有明確的目的並專注於單一任務。

  • 避免模塊過大:將大模塊分解為更小、更集中的組件。

  • 使用描述性命名:為類、變量和方法選擇有意義的名稱。

  • 最小化全局變量:避免過度使用全局變量,因為它們可能會引入緊密耦合。

始終設置 NODE_ENV=production

雖然NODE_ENV=production通常用於在 Node.js 應用程序中啓用優化和特定行為,但濫用它可能會導致開發、測試和登台環境中出現不一致和挑戰。

為什麼 NODE_ENV 是一種有缺陷的方法
環境是一個數字平台或系統,工程師可以在其中構建、測試、部署和管理軟件產品。傳統上,我們的應用程序運行在四個階段或類型的環境中:


  • 開發:用於構建、測試和調試應用程序的本地或共享環境。
  • 測試:專用於在受控環境中測試應用程序的環境,通常採用自動化測試。

  • 暫存:類似於生產的環境,用於在部署到生產之前進行最終測試和質量保證。

  • 生產:最終用户可以訪問應用程序的實時環境。NODE_ENV

    的根本問題在於開發人員將優化和軟件行為與其軟件運行的環境相結合。結果是如下代碼:

if (process.env.NODE_ENV === 'development') {
  // ...
}

if (process.env.NODE_ENV === 'production') {
  // ...
}

if (['production', 'staging'].includes(process.env.NODE_ENV)) {
  // ...
}

雖然這看起來無害,但它使生產環境和暫存環境不同,從而無法進行可靠的測試。

例如,當 NODE_ENV 設置為 development 時,測試以及產品的功能可能會通過,但當將NODE_ENV 設置為 production時,測試會失敗。

因此,將 NODE_ENV 設置為 production 以外的任何值都被視為反模式。

企業 Node.js 配置最佳實踐

  • 功能標誌:根據專用環境變量啓用或禁用功能,從而可以對應用程序行為進行精細控制。

  • 專用環境變量:為每個部署階段(開發、測試、登台、生產)創建單獨的環境變量,以管理每個階段特定的配置。

  • 機密管理工具: 利用專用的機密管理解決方案來存儲並安全訪問敏感信息,例如 API 密鑰和數據庫憑據。這些工具可以與你的部署流程集成,以在運行時注入機密。在這裏,深入瞭解環境變量在 Node.js 應用程序中的工作原理非常重要——這可能會有所幫助。
  • 與環境無關的配置:努力使代碼根據運行時加載的配置適應不同的環境,儘量減少對特定於環境的邏輯的需要。

  • 注意 CI/CD 管道的演變:相應地調整環境配置。請記住,雖然暫存和生產理想情況下應該相同,但開發環境通常可以具有不同的配置,以方便本地開發和測試。


為什麼要遵循這個原則呢?

  • 提高可靠性:所有環境中的一致行為降低了部署期間出現錯誤的風險。

  • 增強的安全性: 敏感數據通過專用的管理工具保護。
  • 減少維護工作量:由於特定於環境的邏輯被最小化,代碼變得更加靈活且更易於管理。

8.處理錯誤並提供有意義的日誌

確保 Node.js 應用程序的穩健性和穩定性取決於實施有效的錯誤處理和日誌記錄實踐。為此,你需要優雅地捕獲和解決意外錯誤,防止應用程序崩潰,並通過詳細的日誌記錄獲得有關應用程序行為的寶貴見解。

讓我們更詳細地瞭解一下:

採用一致的錯誤處理模式

錯誤處理是 Node.js 開發的一個重要方面。它涉及捕獲和管理應用程序執行期間可能發生的錯誤,防止崩潰並向用户提供有意義的反饋。通過實施有效的錯誤處理機制,你可以增強 Node.js 應用程序的可靠性和可維護性。

處理全局未捕獲異常:

為確保你的應用程序不會意外崩潰,處理全局未捕獲異常至關重要。這涉及使用process.on('uncaughtException')事件偵聽器來捕獲和記錄這些異常。然後,你可以採取適當的措施,例如正常關閉應用程序或向監控系統發送警報。

“優雅關閉:”

處理未捕獲異常時,請考慮實施正常關閉過程,以確保正確清理資源並避免突然終止。你可以使用close-with-grace等庫來註冊關閉處理程序,並在應用程序退出前執行關閉數據庫連接或處理正在進行的請求等任務。

提供有意義的日誌

在 Node.js 開發中,日誌記錄非常重要,因為它可以幫助開發人員監控應用程序事件、跟蹤性能,並在應用程序中出現錯誤或系統故障時收到警報。

日誌記錄在生產環境中也非常有用,可以快速檢測和修復錯誤源。此外,日誌記錄通過提供應用程序行為和性能的清晰視圖,為開發人員提供了增強的可視性。

記錄什麼:

在 Node.js 中記錄時,必須在提供足夠的信息進行故障排除和避免過多的日誌記錄(可能影響性能)之間取得平衡。

以下是一些關鍵注意事項:

  • 錯誤日誌:以適當的詳細程度(例如,調試、信息、警告、錯誤)記錄錯誤,以有效地跟蹤和解決問題。
  • 請求和響應信息:記錄傳入請求和傳出響應,包括 HTTP 方法、URL、請求參數和響應狀態代碼。這有助於識別和排除潛在問題。

  • 性能指標:記錄響應時間、請求持續時間和資源利用率等性能指標,以監控應用程序性能並識別瓶頸。

  • 應用程序狀態:記錄重要的應用程序狀態變化或事件,以跟蹤應用程序的行為並瞭解導致錯誤或意外結果的事件序列。

  • 自定義日誌消息:創建自定義日誌消息,提供特定於你的應用程序的有意義的上下文和信息。


記錄多少:

適當的記錄級別取決於你的特定需求和應用程序的複雜性。請考慮以下準則:


  • 生產環境:在生產環境中,以較低級別(例如信息、警告、錯誤)記錄日誌,以避免過多的日誌記錄影響性能。重點記錄錯誤、關鍵事件和性能指標。

  • 開發和測試:在開發和測試環境中,你可以在更高級別(例如,調試)進行日誌記錄,以收集更詳細的信息,用於故障排除和調試目的。

日誌庫

我個人推薦的日誌記錄工具是Pino,這是一個開銷非常低的 Node.js 日誌記錄器,於 2014 年首次發佈。自發布以來,它就因其速度、效率和靈活的配置而廣受歡迎。

Pino 對 Express、Fastify、Nest、Restify、Hapi、Koa 和核心 Node 等其他 Web 框架提供一流的支持,並且是 Fastify 的默認記錄器。

開發人員為什麼選擇 Pino?

首先,Pino 是異步運行的;這意味着如果目的地繁忙,它會累積日誌,而當目的地可用時,它會立即發送日誌。

其次,Pino 的速度比其他大多數替代方案快五倍左右。為了達到這樣的速度,Pino 使用最少的資源進行日誌記錄,因此,日誌消息會隨着時間的推移而增加,從而對應用程序產生節流效應,例如每秒請求數減少。

此外,Pino 不是一個獨立的工具,因為它支持與 Webpack 和 esbuild 等工具捆綁在一起,為開發人員提供更多的集成選項,從而提供創造的機會。

-馬特奧·科利納


9. 使用API規範並自動生成客户端

在多團隊環境中,建立清晰一致的 API 規範對於有效協作和集成至關重要。通過定義 API 的結構、數據類型和操作,你可以確保不同的團隊能夠獨立工作,同時保持兼容性並避免誤解。此外,自動根據 API 規範生成客户端代碼可以顯著簡化開發並降低出錯風險。

API優先與代碼優先

在設計和開發 API 時,有兩種主要方法:API First 和 Code First。每種方法都有各自的優點和注意事項。


  • API First:在編寫實現代碼之前定義 API 規範。這種方法提倡契約驅動的開發風格,確保客户端和服務器團隊都清楚瞭解 API 的功能和期望。

  • 代碼優先:首先開發實現代碼,然後根據現有代碼生成 API 規範。雖然這種方法可以更快地啓動,但它可能會引入不一致,並使保持 API 和實現之間的一致性變得更加困難。

OpenAPI 與 GraphQL

OpenAPI和GraphQL都是定義和與 API 交互的強大工具,但它們的用途不同,且具有不同的優勢。

開放API:

  • 一種廣泛使用的 RESTful API 規範語言。

  • 定義 API 的結構、數據類型和操作。

  • 非常適合具有預定義 API 契約的場景。

GraphQL:

  • 一種提供更多靈活性和效率的 API 查詢語言。

  • 允許客户端準確指定他們需要的數據,減少過度獲取和獲取不足。

生成客户端和類型

根據 API 規範自動生成客户端和類型可以顯著簡化開發並降低出錯風險。通過利用OpenAPI Generator 或 Swagger Codegen(適用於OpenAPI 規範)和 graphql-codegen(適用於GraphQL)等工具,你可以自動執行以前耗時且容易出錯的任務,加快開發週期,降低類型相關錯誤的風險,並提高代碼質量和可維護性。此外,自動類型生成可以幫助確保 API 定義和客户端代碼之間的一致性,從而降低出現兼容性問題的可能性。

通過採用 API 規範並自動生成客户端,你可以簡化開發,提高代碼質量,並在多團隊環境中促進協作。


總結

通過遵循這些指導原則,你將能夠設置 Node.js 應用程序以獲得長期成功。從掌握事件循環到實現強大的依賴項管理,這些支柱旨在幫助你構建可擴展、安全且可維護的解決方案。

請記住,Node.js 社區依靠協作而蓬勃發展——積極貢獻並支持關鍵項目,以確保所有人都能擁有一個繁榮的生態系統。

有了這些知識,我們希望看到你提升 Node.js 開發水平並構建經得起時間考驗的出色應用程序。

user avatar toopoo 头像 grewer 头像 haoqidewukong 头像 zaotalk 头像 smalike 头像 yinzhixiaxue 头像 nihaojob 头像 jingdongkeji 头像 qingzhan 头像 kobe_fans_zxc 头像 dirackeeko 头像 aqiongbei 头像
点赞 270 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.