我們在 RudderStack 使用的開發(fā)方式之一是安全快速地構(gòu)建,然后根據(jù)需要進行優(yōu)化,,這種模式使我們能夠優(yōu)先考慮客戶問題,,跟上 RudderStack 的快速增長的腳步。 但在某些情況下,,這種方式會導(dǎo)致開發(fā)體驗的流失,。發(fā)生這種情況時,我們使用帕累托原則重新集中精力,,力求在消除技術(shù)債務(wù)中投入的時間能得到最大的回報,。 這種不太好的開發(fā)體驗的一個例子是 Control Plane 的主后端服務(wù)的部署時間過長。過去在生產(chǎn)環(huán)境中部署需要 5 分鐘,,更甚的是,,在開發(fā)過程中,根據(jù)硬件的不同,,重啟需要 40-90 秒,,這成了一個主要的痛點,拖慢了我們團隊的進度,,我們知道,,是時候重新關(guān)注和解決它了,我們是這樣做的,。 首先,我解釋一下我所說的“Control Plane(控制臺)”,,Rudderstack 的架構(gòu)分為兩部分:數(shù)據(jù)臺和控制臺,。控制臺是 Rudderstack 平臺的大腦,,它是存儲資源和配置的地方,,你的組織、工作區(qū),、基礎(chǔ)設(shè)施和賬單中的用戶管理和協(xié)作都在控制臺中進行,。 從架構(gòu)的角度來看,控制臺由一個以集群模式運行的后端應(yīng)用,、幾個附屬微服務(wù)和一個前端應(yīng)用組成,。對于我們的后端服務(wù),我們使用 Node.js 和 Typescript,,用 ts-node 來啟動和運行應(yīng)用程序,。但是如上所述,,這是有代價的,讓我們深入了解里面發(fā)生了什么,。 我們知道 Node.js 不是問題的原因,,原生的 HTTP 服務(wù)器幾乎是立即重啟,我們使用的 koa web 框架精簡且輕量級,。所以,,我們需要做一些分析來查明原因,使用 clinic.js 來幫助分析,,它簡單而易用,。 果然,在設(shè)置好 clinic 并進行了幾次測試運行之后,,我們生成了一些火焰圖(火焰圖是一種顯示每個方法和依賴項需要多少執(zhí)行(CPU)時間的方式),,它們揭示了問題。 帶有源代碼和過程的火焰圖: 沒有源代碼的過程火焰圖: 不管是否包含 rudder-config-backend 源代碼,,圖表都是一樣的,,所以我們知道源代碼不是問題,并且可以確定開銷來自 Typescript,,尤其是 ts-node,。 這是有道理的,因為每當(dāng)進程重新啟動時,,整個源代碼都必須從零開始轉(zhuǎn)換為 Javascript,,而且沒有任何緩存;這與我們在集群模式下部署服務(wù)器時遇到的較大延遲一致,。每個工作進程都必須獨立編譯 Typescript 文件,,因此重新啟動需要很多時間,有時還會導(dǎo)致資源匱乏,。具體來說,,我們在服務(wù)器啟動期間,可以看到內(nèi)存不足錯誤和 CPU 利用率在增加,。 雖然在生產(chǎn)中使用 ts-node 并不是一種壞的做法 (如果設(shè)置得當(dāng)),,但在我們的案例中,我們意識到它會產(chǎn)生大量的開銷,,然而我們嚴重依賴 TypeORM 和 reflect-metadata,,這使得 ts-node 很有吸引力。消除這種依賴需要大量的工作,,并可能通過限制我們的工具集而導(dǎo)致 DX 的進一步退化,。所以,我們只有一個選擇:刪除 Typescript,。 當(dāng)然,,不是完全刪除 Typescript,,只是在生產(chǎn)環(huán)境。至少在理論上,,讓一個 node 進程加載.js 文件,而不是用 ts-node 包裝器,,這將大大減少啟動時間,,正如我們在第二個火焰圖中觀察到的那樣。當(dāng)然,,我們可以采取不同的方法來實現(xiàn)這一點,,但每一種方法都有利弊。 我們最初的方法是使用 tsc 二進制文件,,和安裝的 Typescript 版本一起打包,,并增加一個編譯步驟。事實證明,,這比想象的更棘手,,因為幾位工程師在 2 年多的時間里用不同的方法開發(fā)了配置的后端。因此,,我們遇到了一些問題:
可以使用幾個補丁來修改 tsc 的行為,,繞過 Typescript 的轉(zhuǎn)譯限制,。不幸的是,這些解決方案雖然不是很復(fù)雜,,但需要需要大量的混合和匹配來覆蓋所有用例,,并且對項目添加了額外的依賴項,例如 typescript-transformer-append-js-extension,。 退一步說,,我們意識到將不得不犧牲 Typescript 模塊提供的一些便利,并重寫應(yīng)用程序的某些部分,,尤其是在導(dǎo)入模塊方面,。 但是,如果有一個解決方案可以找出依賴關(guān)系,,以及如何以聲明的方式導(dǎo)入它們呢? webpack 是一個傳統(tǒng)的 JavaScript 模塊打包器,,創(chuàng)建的目的是通過有效地將前端應(yīng)用分割成塊,,快速地將其傳送到用戶的瀏覽器。作為最古老,、最成熟的打包工具之一,,至今仍在積極地維護中,webpack 擁有一個龐大的插件生態(tài)系統(tǒng),,適應(yīng)任何類型的復(fù)雜應(yīng)用,,并且它對 Node.js 提供了一流的支持。 由于 webpack 就是為此目的而構(gòu)建的,,讓它來處理模塊解析和轉(zhuǎn)換.ts 文件,,相比其它類 hack 和猴子補丁方法,感覺更自然,。我們努力了幾次讓 webpack 與 TypeORM 一起工作,,主要是因為 TypeORM 頑固的設(shè)定。例如,,數(shù)據(jù)庫遷移文件必須在類名末尾包含時間戳,,這意味著源文件不能縮小,導(dǎo)入 / 導(dǎo)出名稱不能被篡改,。但經(jīng)過幾次嘗試,,我們成功了。果然,,通過 webpack 及其插件處理,,每個文件都簡化了構(gòu)建過程。通過高效緩存,,后續(xù)構(gòu)建的速度會更快,,從而獲得更好的 DX 和更短的部署窗口。集群模式的部署現(xiàn)在大約需要 12 秒,,縮短了近 5 分鐘,!——從服務(wù)請求開始。請記住,,這是 8 個節(jié)點進程共享的資源,,每個節(jié)點進程啟動一個 koa 的 web 服務(wù)器和通過 TypeORM 連接到數(shù)據(jù)庫。 在開發(fā)過程中,,結(jié)果更加突出:
以下是我們用來大幅減少啟動時間的 webpack 配置:
npm install --save-dev webpack webpack-cli @types/webpack-env webpack 和 webpack-cli 不言自明,,第三個包 @types/webpack-env,會啟用 webpack 的 require.Context 的自動完成功能,,這需要手動指導(dǎo) webpack 如何以元編程的方式處理符號,,例如,在源代碼目錄中找到你的 ORM 實體并自動聲明它們,,而不是專門地一個個導(dǎo)入——我們有大量這樣的實體,! 注意:所有這些依賴項只能在開發(fā)和構(gòu)建期間使用,,不需要在生產(chǎn)構(gòu)建中加載它們!
webpack 的配置非常簡單,,只需在你的項目根目錄(通常是 package.json 所在的文件夾)中創(chuàng)建一個 webpack.config.js 文件,,然后導(dǎo)出 webpack 配置,。它看起來可能像這樣:
module.exports = { entry: './src/index.ts', // the file you would provide to ts-node or node binaries for execution mode: NODE_ENV, // development or production target: 'node', // webpack works differently based on target, here we use node.js output: { // directions for the built files directory path: path.resolve(__dirname, 'dist'), filename: 'index.js', }, }
對于這一步,,你可以安裝任何你喜歡的 webpack 的 typescript 加載器。我們使用 ts-loader: npm install --save-dev ts-loader
npm install --save-dev webpack-node-externals
module.exports = { // ... plugins: [ ...plugins, ], } 下面是我們使用的一些插件的列表,,向出色的貢獻者和維護者致敬,!
你最終的 webpack 配置應(yīng)該是這樣的:
我們從運行時的依賴項中刪除了 Typescript,,所以我們在最終的生產(chǎn)制品中不再需要它,,這樣我們完全擺脫了這些依賴! 它也啟發(fā)我們優(yōu)化了構(gòu)建流水線,,通過引入帶緩存層,、和為開發(fā)和生產(chǎn)不同目標(biāo)的多階段 docker 構(gòu)建,使其更為高效,。 更少的依賴意味著:
最重要的是,它意味著面臨更少的攻擊,,由于依賴更少,、審計和解決漏洞的時間更少,讓 RudderStack 對我們的客戶來說更加安全,。 原文鏈接: https://www./blog/how-we-reduced-startup-time-by-80-with-webpack/ |
|