Rust 語言案例研究:社群使得 Rust 成為 npm 的簡單選擇
使用 Rust 來解決 CPU 效能產生的 npm 軟體包註冊瓶頸
Rust 語言在 npm
事實與資料:
共有超過 836,000 個 JavaScript 包可用
每個月使用 npm 下載的軟體包超過三百億
下載來自於超過一千萬個使用者
複製程式碼
npm 公司(npm, Inc.)是 npmjs.com 網站和 npm 註冊服務背後的公司。它是全世界最大的軟體註冊中心,每天有高達 13 億的包下載量。除了維護開源軟體包註冊,npm 公司也提供企業級的軟體包註冊產品,並維護著一個用於與註冊資訊互動的命令列工具。
npm 所面臨的挑戰是對效率和擴充套件性的要求。如果服務部署後可以被遺忘(譯註:指服務不需要花精力維護),我們就節約了寶貴的時間,可以處理別的問題。npm 員工們也很希望,他們所使用的技術有一個有幫助的社群。Rust 符合這些要求,而且在 npm 目前的技術棧中也有部分使用。
問題:擴充套件一個受限於 CPU 效能的服務
npm 工程團隊時刻關注著隨著 npm 包指數增長可能導致的效能問題。大多數情況下 npm 的操作效能受限於網路,JavaScript 足以支撐實現效能目標。然而,決定使用者是否被允許釋出軟體包的認證服務,是一個受限於 CPU 效能的任務,預計會成為效能瓶頸。
Node.js 中使用傳統 JavaScript 實現的這個服務無論如何都需要重寫,所以 npm 團隊藉此契機,考慮在服務降級前重新實現,既使得程式碼更現代化,又能提高服務效能。
考慮過的解決方案
當考慮替代技術時,npm 團隊很快否決了 C,C++和 Java 實現,而密切關注 Go 語言和 Rust 語言。
在 npm 工程團隊的考慮中,C 或 C++ 解決方案不再是一個合理的選擇。“我不相信自己能寫一個 C++ HTTP 應用程式,然後把它釋出到網路上,”npm 的工程師 Chris Dickinson 解釋說。這些語言需要記憶體管理方面的專業知識,以避免可能導致災難性問題的錯誤。即使是為了提高效能,npm 公司也無法容忍安全問題、系統崩潰和記憶體洩漏的發生。
Java 也未被考慮,因為要部署任何程式到生產伺服器上,都得帶上 JVM(Java 虛擬機器)和相關類庫。這是一系列複雜操作,會產生資源消耗,與 C 或 C++的不安全性一樣難以接受。
考慮到這些要求,所選的程式語言應該:
- 記憶體安全
- 可以編譯為獨立、易部署的二進位制檔案
- 效能始終優於 JavaScript
因此最後考慮使用的是 Go 語言和 Rust 語言。
評估
為了評估候選方案,npm 團隊分別使用 Node.js、Go 語言和 Rust 語言重寫了授權服務。
使用 Node.js 重寫的服務被當做基準線,以評估使用 Go 語言和 Rust 語言帶來的變化。如你所料,npm 有很多 JavaScript 專家。使用 Node.js 重寫授權服務大約用了一個小時,效能表現與過去實現的差不多。
使用 Go 語言重寫授權服務用了 2 天。在這個過程中,npm 團隊對於缺少依賴管理的解決方案感到失望。npm 公司致力於讓 JavaScript 依賴管理可預測,並且用起來毫不費力,他們期待其他語言生態也有同樣的世界級依賴管理工具。Go 語言的依賴安裝全球化程式、跨專案版本共享的前景(在他們進行評估時 Go 語言的標準)都不具有吸引力。
當開始用 Rust 語言實現時,他們在依賴管理方面發現了鮮明的對比。“Rust 語言有著絕對令人驚歎的依賴管理,”一位工程師熱情地說,他指出 Rust 的依賴管理策略從 npm 獲得了靈感。Rust 語言安裝時附帶的 Cargo 命令列工具與 npm 命令列工具類似:Cargo 分開協調每一個專案中的各依賴版本,從而讓搭建每個專案時所在環境不會影響最終的執行結果。Rust 語言的開發體驗很友好,符合 npm 團隊受到 JavaScript 影響的預期。
Rust 語言有著絕對令人驚歎的依賴管理。
使用 Rust 語言重寫授權服務的時間其實比 JavaScript 版本和 Go 語言版本的都長:大約用了一週來熟悉語言並進行實現。Rust 感覺像是一種更難以搞定的程式語言。語言的設計預先載入了不同於其他常見程式語言的記憶體使用決策,以確保記憶體安全性。“你可以寫出一個正確的程式,但是你必須考慮到這個程式的方方面面,”Dickinson 說。然而,當工程師們遇到問題時,Rust 社群友善地回答了問題,很有幫助。這一點讓 npm 團隊能夠用 Rust 語言重寫服務並將其部署到生成環境。
結果
我對 Rust 最大的恭維就是它很“無聊”。
npm 的第一個 Rust 程式在一年半的生產使用中沒有引起任何警報。“我對 Rust 最大的恭維就是它很‘無聊’,”Dickinson 說,“而這是一個很棒的恭維。”部署新的 Rust 服務的過程很簡單,很快他們就忘記了這個 Rust 服務,因為它極少引起執行問題。在 npm 公司,將 JavaScript 服務部署到生產環境的一般經驗是,要對服務的錯誤和過多的資源佔用進行大量監控,從而需要除錯和重啟。
社群很重要
npm 稱 Rust 社群是決策過程中的一個積極因素。他們認為 Rust 社群很有價值的一些方面是他們的包容、友好以及做出困難的技術決策的可靠流程。這些方面使 Rust 語言的學習和解決方案的開發更容易,並保證了語言將以健康、可持續的方式繼續提高。
缺點:維護多個技術棧
每個技術決策都需要權衡利弊,將 Rust 語言加入 npm 的生產服務也不例外。將 Rust 引入 npm 的最大缺點是,現有的 JavaScript 技術棧和新的 Rust 技術棧都有自己的解決方案,用來進行狀態監視、日誌記錄與發出警報;而維護這些解決方案有一定的負擔。作為一門年輕的語言,Rust 還沒有用以完成工業級產品標準的庫和最佳實踐,但是希望在不久的將來能實現。
結論
Rust 語言是一種可擴充套件且易於部署的解決方案。它可以佔用較少的資源而不影響記憶體安全性。通過 Cargo 的依賴管理為系統程式設計領域帶來了現代化工具。雖然仍有最佳實踐和工具有待提高,但社群的建立保證了長期成功。出於這些原因,npm 選擇了 Rust 來處理 CPU 限制的效能瓶頸。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。