起運(yùn)港:
目的港:
國(guó)際空運(yùn)
國(guó)際海運(yùn)
國(guó)際快遞

初步解決方案:利用 Node Require Cache“熱重載”應(yīng)用程序代碼

?新聞 ????|???? ?2020-04-18 11:28

幾個(gè)月前,我們留意到,銀行集成服務(wù)部署緩慢正在影響團(tuán)隊(duì)發(fā)布代碼的能力。工程師要花至少 30 分鐘才能通過多個(gè)過渡環(huán)境和生產(chǎn)環(huán)境構(gòu)建、部署和監(jiān)視變更,這將消耗大量寶貴的工程時(shí)間。隨著團(tuán)隊(duì)越來越大,我們天天發(fā)布的代碼也越來越多,這一點(diǎn)變得越來越不可接受。

固然我們計(jì)劃實(shí)現(xiàn)長(zhǎng)期改進(jìn),比如將基于 Amazon ECS 服務(wù)的基礎(chǔ)設(shè)施遷移到 Kubernetes 上,但是,為了在短期內(nèi)進(jìn)步迭代速度,有必要快速解決下這個(gè)題目。因此,我們決定實(shí)踐自定義的“快速部署”機(jī)制。

我們的銀行集成服務(wù)由 4000 個(gè) Node.js 進(jìn)程組成,這些進(jìn)程運(yùn)行在專用的 Docker 收留器上,這些收留器托管并部署在 Amazon 的收留器編排服務(wù) ECS 上。在分析了我們的部署過程之后,我們將增加的部署延遲回結(jié)到三個(gè)不同的組件上:

啟動(dòng)任務(wù)會(huì)導(dǎo)致延遲。除了應(yīng)用程序啟動(dòng)時(shí)間之外,ECS 健康檢查也會(huì)導(dǎo)致延遲,它決定收留器何時(shí)預(yù)備好開始處理流量??刂七@個(gè)過程的三個(gè)參數(shù)是 interval、retry 和 螺螄粉 startPeriod。假如沒有對(duì)健康檢查進(jìn)行仔細(xì)調(diào)優(yōu),收留器可能會(huì)卡在“啟動(dòng)”狀態(tài),即使它們已經(jīng)預(yù)備好為流量服務(wù)。封閉任務(wù)會(huì)導(dǎo)致延遲。當(dāng)我們運(yùn)行 ECS 服務(wù)更新時(shí),一個(gè) SIGTERM 信號(hào)被發(fā)送到所有正在運(yùn)行的收留器。為了處理這個(gè)題目,我們?cè)趹?yīng)用程序代碼中使用了一些邏輯,以便在完全封閉服務(wù)之前占用現(xiàn)有資源。我們啟動(dòng)任務(wù)的速度限制了部署的并行性。盡管我們將 MaximumPercent 參數(shù)設(shè)置為 200%,但是 ECS start-taskAPI 調(diào)用的硬限制是每個(gè)調(diào)用只能執(zhí)行 10 個(gè)任務(wù),千航國(guó)際,而且速度有限。我們需要調(diào)用 400 次才能將所有收留器投進(jìn)生產(chǎn)。

我們考慮并試驗(yàn)了一些不同的潛伏解決方案,以逐步實(shí)現(xiàn)總體目標(biāo):

減少生產(chǎn)中運(yùn)行的收留器總數(shù)。這當(dāng)然是可行的,但它涉及到對(duì)服務(wù)架構(gòu)進(jìn)行重大修改,以使其能夠處理相同的請(qǐng)求吞吐量,在進(jìn)行這樣的修改之前,還需要進(jìn)行更多研究。通過修改健康檢查參數(shù)來調(diào)整 ECS 配置。我們嘗試通過減少 interval 和 startPeriod 的值來加強(qiáng)健康檢查,但是 ECS 在啟動(dòng)時(shí)將健康的收留器錯(cuò)誤地標(biāo)記為不健康,導(dǎo)致我們的服務(wù)永遠(yuǎn)無法完全穩(wěn)定在 100% 健康狀態(tài)。由于根本題目(ECS 部署緩慢)依然存在,對(duì)這些參數(shù)進(jìn)行迭代是一個(gè)緩慢而費(fèi)力的過程。在 ECS 集群中啟動(dòng)更多實(shí)例,以便可以在部署期間同時(shí)啟動(dòng)更多任務(wù)。這樣做可以減少部署時(shí)間,但不會(huì)減少太多。從長(zhǎng)遠(yuǎn)來看,這也不劃算。通過重構(gòu)初始化和關(guān)機(jī)邏輯優(yōu)化服務(wù)重啟時(shí)間。只需要做一些小小的修改,我們就能夠在每個(gè)收留器中節(jié)省大約 5 秒的時(shí)間。

盡管這些更改將總體部署時(shí)間減少了幾分鐘,但是我們?nèi)匀恍枰獙r(shí)間進(jìn)步至少一個(gè)數(shù)目級(jí),才能以為題目已解決。這將需要一個(gè)根本不同的解決方案。

Node require cache 是一個(gè) JavaScript 對(duì)象,它根據(jù)需要緩存模塊。這意味著多次執(zhí)行 require(‘foo’) 或 import * as 螺螄粉 foo from 'foo’只會(huì)在第一次時(shí)請(qǐng)求 foo 模塊。神奇的是,刪除 require cache 中的條目(我們可以使用全局 require.cache 對(duì)象訪問)將迫使 Node 在下次導(dǎo)進(jìn)模塊時(shí)從磁盤重新讀取該模塊。

為了繞過 ECS 部署過程,我們嘗試使用 Node 的 require cache 在運(yùn)行時(shí)執(zhí)行應(yīng)用程序代碼的“熱重載”。一旦接收到外部觸發(fā)(我們將實(shí)在現(xiàn)為銀行集成服務(wù)上的 gRPC 端點(diǎn)),應(yīng)用程序?qū)⑾螺d新代碼來替換現(xiàn)有的構(gòu)建,清除 require cache,從而強(qiáng)制重新導(dǎo)進(jìn)所有相關(guān)模塊。通過這種方法,我們能夠消除 ECS 部署中存在的大部分延遲,優(yōu)化整個(gè)部署過程。

在 Plaiderdays (我們的內(nèi)部黑客馬拉松)期間,來自不同團(tuán)隊(duì)的一組工程師聚在一起,為我們所謂的“快速部署”實(shí)現(xiàn)了一個(gè)端到真?zhèn)€概念驗(yàn)證。當(dāng)我們一起想法構(gòu)建一個(gè)原型時(shí),有一件事似乎出了題目:假如下載新構(gòu)建的 Node 代碼也試圖使失效緩存,跨境鐵路 國(guó)際物流,那么下載器代碼本身將如何重新加載就不清楚了。(有一種方法可以解決這個(gè)題目,就是使用 Node EventEmitter ,但是會(huì)給代碼增加相當(dāng)大的復(fù)雜性)。更重要的是,千航國(guó)際,還存在運(yùn)行未同步代碼版本的風(fēng)險(xiǎn),這可能導(dǎo)致應(yīng)用程序意外失敗。

由于我們不愿意在銀行集成服務(wù)的可靠性上妥協(xié),這種復(fù)雜性需要重新考慮“熱重載”方法。

在過往,為了在所有服務(wù)中運(yùn)行一系列同一的初始化任務(wù),我們編寫了自己的進(jìn)程封裝器,它的名稱非常貼切,叫做 Bootloader。Bootloader 的核心包含設(shè)置日志管道、轉(zhuǎn)發(fā)信號(hào)和讀取 ECS 元數(shù)據(jù)的邏輯。每個(gè)服務(wù)都是通過將應(yīng)用程序可執(zhí)行文件的路徑以及一系列標(biāo)志傳遞給 Bootloader 來啟動(dòng)的,這些文件在執(zhí)行初始化步驟之后會(huì)作為子進(jìn)程執(zhí)行。

我們沒有清除 Node 的 require cache,而是在下載預(yù)期的部署構(gòu)建后,使用特殊的退出代碼來調(diào)用 process.exit 實(shí)現(xiàn)服務(wù)更新。我們還在 Bootloader 中實(shí)現(xiàn)了自定義邏輯,以觸發(fā)使用此代碼退出的任何子進(jìn)程的進(jìn)程重載。與“熱重載”方法類似,這使我們能夠繞過 ECS 部署的本錢并快速引導(dǎo)新代碼,同時(shí)避免“熱重載”的陷阱。此外,Bootloader 層的這種“快速部署”邏輯答應(yīng)我們將其推廣到在 Plaid 運(yùn)行的任何其他服務(wù)。

下面是終極解決方案:

Jenkins 部署管道向銀行集成服務(wù)的所有實(shí)例發(fā)送 RPC 請(qǐng)求,指示它們“快速部署”特定的提交散列。應(yīng)用程序接收 gRPC 請(qǐng)求進(jìn)行快速部署,并根據(jù)接收到的提交散列從 Amazon S3 下載構(gòu)建好的壓縮包。然后,它替換文件系統(tǒng)上的現(xiàn)有構(gòu)建,并使用 Bootloader 識(shí)別的特殊退出代碼退出。Bootloader 看到應(yīng)用程序使用這個(gè)特殊的“Reload”退出代碼退出,然后重新啟動(dòng)應(yīng)用程序。服務(wù)運(yùn)行新的代碼。

下面這張圖簡(jiǎn)單說明了這個(gè)過程。

結(jié)果

我們能夠在 3 周內(nèi)交付這個(gè)“快速部署”項(xiàng)目,并將 90% 生產(chǎn)收留器的部署時(shí)間從 30 多分鐘減少到 1.5 分鐘。

上圖顯示了我們?yōu)殂y行集成服務(wù)部署的收留器數(shù)目(按提交表示為不同的顏色)。假如留意下黃線,就可以看到它在 12:15 左右增長(zhǎng)趨于平穩(wěn),這代表我們的收留器長(zhǎng)尾仍然在占用資源。

這個(gè)項(xiàng)目極大進(jìn)步了 Plaid 集成工作的速度,答應(yīng)我們更快地發(fā)布特性及進(jìn)行 Bug 修復(fù),并將浪費(fèi)在上下文切換和監(jiān)視儀表板上的工程時(shí)間最小化。這也證實(shí)了我們的工程文化,即通過黑客馬拉松得來的想法實(shí)現(xiàn)具有實(shí)質(zhì)性影響的項(xiàng)目。

最后,我自己是一名從事了多年開發(fā)的JAVA老程序員,辭職目前在做自己的java私人定制課程,今年年初我花了一個(gè)月整理了一份最適合2019年學(xué)習(xí)的java學(xué)習(xí)干貨,可以送給每一位喜歡java的小伙伴,想要獲取的可以關(guān)注我的頭條號(hào)并在后臺(tái)私信我:java,即可免費(fèi)獲取。

本文轉(zhuǎn)載至微信公眾號(hào)——InfoQ,如有侵權(quán)請(qǐng)聯(lián)系立刪!


袋裝螺螄粉 袋裝螺螄粉

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

千航國(guó)際
國(guó)際空運(yùn)
國(guó)際海運(yùn)
國(guó)際快遞
跨境鐵路
多式聯(lián)運(yùn)
起始地 目的地 45+ 100 300 詳情
深圳 迪拜 30 25 20 詳情
廣州 南非 26 22 16 詳情
上海 巴西 37 28 23 詳情
寧波 歐洲 37 27 23 詳情
香港 南亞 30 27 25 詳情

在線咨詢-給我們留言