国产粉嫩无码不卡在线观看,酒店大战丝袜高跟鞋人妻,特级精品毛片免费观看,欧美亚洲日本国产综合在线

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

作者介紹

馬陽陽:去哪兒網(wǎng)基礎(chǔ)架構(gòu)組資深開發(fā)工程師、公司云原生 SIG 成員,專注于云原生、效能領(lǐng)域。負(fù)責(zé)組件市場、測試環(huán)境治理平臺 Noah、代碼瘦身平臺。優(yōu)化過 Noah,使環(huán)境構(gòu)建成功率提升 35%、耗時降低 30%。22 年深度參與的“線上服務(wù)瘦身50%”項(xiàng)目獲得公司級技術(shù)型一等獎,指導(dǎo)多個團(tuán)隊(duì)完成系統(tǒng)精簡,積累了大量經(jīng)驗(yàn)。畢業(yè)后曾就職于 BES,從事 PaaS 云、DevOps 平臺開發(fā)工作。

一、背景

去哪兒早在 05 年就開始做機(jī)票相關(guān)業(yè)務(wù),在這十幾年間業(yè)務(wù)快速發(fā)展,后端系統(tǒng)也在不斷迭代,目前公司內(nèi)擁有大量五年以上的系統(tǒng)。為適應(yīng)快速變化,公司的組織架構(gòu)調(diào)整和人員流動較多,導(dǎo)致目前開發(fā)者們維護(hù)的系統(tǒng),大部分都是交接過來的,對系統(tǒng)全貌掌控力有限。

另一方面,公司的很多業(yè)務(wù)具備短周期特征,比如臨近五一假期了,我們會在去哪兒旅行 App 上增加五一相關(guān)的活動業(yè)務(wù)。一旦五一假期結(jié)束后,這些業(yè)務(wù)會立刻被下掉,不再產(chǎn)生實(shí)際價值。而對應(yīng)的后端代碼并不會清理,這就產(chǎn)生了一個有趣的現(xiàn)象,我們的代碼只增不減、系統(tǒng)復(fù)雜度單調(diào)遞增。

隨著時間的推移,維護(hù)和優(yōu)化現(xiàn)有代碼的耗時會越來越多,導(dǎo)致開發(fā)新業(yè)務(wù)的時間減少,業(yè)務(wù)開發(fā)越來越慢,跟不上業(yè)務(wù)發(fā)展的速度,急需解決!

從數(shù)據(jù)上看,公司有數(shù)千個常用應(yīng)用,數(shù)千萬行代碼,平分到每個開發(fā)者頭上,人均要維護(hù)多個應(yīng)用、十幾萬行代碼,可以感受到這個維護(hù)負(fù)擔(dān)是比較重的。

如何解決維護(hù)成本高、業(yè)務(wù)迭代慢的痛點(diǎn)呢?我們在去年全年開展了系統(tǒng)瘦身項(xiàng)目,制定了兩個目標(biāo)指標(biāo),對全司代碼、全司應(yīng)用均精簡 50%,也就是都砍掉一半。要完成這個目標(biāo),有三個很大的挑戰(zhàn):

  • 目標(biāo)定的非常高,需要在一年內(nèi)刪除掉千萬余行代碼;
  • 這是全司級別的項(xiàng)目,牽扯幾十個業(yè)務(wù)團(tuán)隊(duì)、幾千個線上應(yīng)用,如何在這么廣的影響范圍下,高效、低風(fēng)險地達(dá)成目標(biāo),將是個挑戰(zhàn);
  • 網(wǎng)上找不到可以參考的案例,這就要求我們要有很強(qiáng)的創(chuàng)造力和技術(shù)實(shí)力。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

要實(shí)現(xiàn)這個目標(biāo),我們做了兩個規(guī)劃:

  1. 時間上,分兩階段,從 5 月份到 6 月底進(jìn)行服務(wù)精簡,從 7 月初到 11 月底進(jìn)行代碼精簡。之所以服務(wù)放在前面,是因?yàn)榉?wù)精簡的同時也會帶來代碼的精簡;
  2. 架構(gòu)上,將整個目標(biāo)分配到各個業(yè)務(wù)線,每個業(yè)務(wù)線完成 50% 精簡的目標(biāo),分而治之。另外,創(chuàng)建了一個虛擬組織“瘦身技術(shù)支撐團(tuán)隊(duì)”,提供通用瘦身工具和技術(shù)支持,加速業(yè)務(wù)線的瘦身效率。本人也是該虛擬組織中的一員。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

接下來依次對服務(wù)精簡、代碼精簡做詳細(xì)介紹,會先分析可以被精簡的資源有什么通用特征,然后利用這些通用特征批量、自動化地找到可能能被精簡的資源全集,這個過程稱之為 “找得到”;有了目標(biāo)全集之后,進(jìn)行正真的精簡,也就是要 “刪得好”。

二、服務(wù)精簡實(shí)戰(zhàn)

1、可精簡服務(wù)特征分析

在進(jìn)行服務(wù)精簡實(shí)戰(zhàn)之前,需要先分析出可精簡服務(wù)所具備的特征,有了一些通用的特征后,就能根據(jù)特征批量找到全部的目標(biāo)服務(wù)。

服務(wù)精簡的手段有兩種,合并服務(wù)和刪除服務(wù),這兩種手段所處理的服務(wù)特征不一樣,因此分開進(jìn)行分析。

合并服務(wù)

哪些服務(wù)能進(jìn)行合并?想直接回答這一問題并不容易,可以反過來思考,哪些服務(wù)要進(jìn)行拆分?開發(fā)同學(xué)對微服務(wù)拆分都很熟悉,常見拆分原則有單一職責(zé)、界限上下文、復(fù)用性、自治性等等,常見服務(wù)拆分維度有 “業(yè)務(wù)” 和 “質(zhì)量”,根據(jù)業(yè)務(wù)拆分的常見手段包括:

  • 按業(yè)務(wù)域:賣票和售后屬于不同業(yè)務(wù),在微服務(wù)架構(gòu)中考慮拆分成兩個微服務(wù)
  • 按業(yè)務(wù)流程:對于訂單業(yè)務(wù),按流程可以拆分成 生單、計(jì)費(fèi)、支付、物流、通知 等多個子服務(wù)
  • 按業(yè)務(wù)重要性:訂單業(yè)務(wù)一般是核心業(yè)務(wù);評價業(yè)務(wù)則沒那么重要,所以訂單和評價通常是兩個微服務(wù)

根據(jù)質(zhì)量拆分的常見手段包括:性能、穩(wěn)定性、可用性、安全邊界、異構(gòu)等。

理論上,規(guī)避了上述服務(wù)拆分特征的服務(wù)就允許進(jìn)行合并。

判斷當(dāng)前服務(wù)是否符合服務(wù)合并的特征,需要對服務(wù)所提供的業(yè)務(wù)有深刻理解才能進(jìn)行判斷,難以通過已有的數(shù)據(jù)資產(chǎn)分析得出,只能是系統(tǒng)開發(fā)者進(jìn)行判斷,因此服務(wù)合并主要由各業(yè)務(wù)線自行推進(jìn)。

刪除服務(wù)

哪些服務(wù)能被直接刪除?從業(yè)務(wù)價值角度很容易得出結(jié)論,無價值甚至低價值服務(wù)可以進(jìn)行刪除,更為具體的表現(xiàn)有三種,滿足其中一種就可能是低價值服務(wù),注意是 “可能而非一定”

  • 沒流量:服務(wù)雖然在線上,但沒有業(yè)務(wù)流量,業(yè)務(wù)價值自然是低的
  • 不迭代:這個特征不好理解,可以通過價值模型加深理解。價值分為 “存量價值” 和 “增量價值”,服務(wù)一旦沒有迭代,就沒有了增量價值。先利用這個特征將服務(wù)掃出來,再人工判斷一下其存量價值,如果存量價值較高那么就不做刪除,反之則反
  • 已下線:服務(wù)已經(jīng)下線了,但沒有被刪除

判斷當(dāng)前服務(wù)是否可被刪除,是可以通過已有數(shù)據(jù)分析得出的,因此作為瘦身基礎(chǔ)支撐團(tuán)隊(duì),提供了兩個通用工具,第一個工具的作用是 “找得到”,即找出符合特征的服務(wù)全集;第二個工具的作用是“刪得好”,自動化將服務(wù)進(jìn)行刪除。最終達(dá)到快速刪除服務(wù)的作用,下面對 “找得到”、“刪得好” 進(jìn)行介紹。

2、找得到

現(xiàn)在需要按照之前分析得出的特征,通過已有數(shù)據(jù)資產(chǎn),批量找到符合特征的服務(wù),這些服務(wù)就是最終要刪除的備選目標(biāo)。

沒流量

將流量全集分為三類:

  • 南北方向流量:南北方向流量通常是指從客戶端到服務(wù)器端的流量,也就是來自于外部用戶或者應(yīng)用的請求流量,它通過負(fù)載均衡器或API網(wǎng)關(guān)被路由到不同的微服務(wù)實(shí)例上
  • 東西方向流量:東西方向流量通常是指微服務(wù)之間的內(nèi)部通信流量,也就是微服務(wù)之間的請求和響應(yīng)流量,這種流量通常發(fā)生在內(nèi)部微服務(wù)的調(diào)用或者微服務(wù)之間的數(shù)據(jù)交換過程中。這里我將東西間流量進(jìn)一步細(xì)分,拆成兩類:
    • 東西方向 – 服務(wù)之間流量
    • 東西方向 – 單服務(wù)內(nèi)流量

三類都沒有流量則可以認(rèn)為服務(wù)是完全沒有流量的,盡可能保證判斷的全面性。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

針對每種類型,可以通過分析現(xiàn)有數(shù)據(jù)資產(chǎn),來得出是否有特定類型的流量。

  1. 根據(jù)網(wǎng)關(guān)的歷史訪問日志,可以找出一段時間內(nèi),沒有南北向流量的服務(wù)集
  2. 根據(jù)歷史 Trace 的拓?fù)湫畔?,可以分析出一段時間內(nèi),沒有東西向服務(wù)間流量的服務(wù)集
  3. 最后,根據(jù)服務(wù)是否有生效的定時任務(wù),可以找出沒有服務(wù)內(nèi)流量的服務(wù)集

最終對這三個集合取交集,即為全部 “沒流量” 的服務(wù)。

不迭代

從業(yè)務(wù)上看,不進(jìn)行服務(wù)迭代有兩種可能,業(yè)務(wù)非常穩(wěn)定了不需要迭代、業(yè)務(wù)不值得再花時間迭代。無論哪種情況,不迭代的具體表現(xiàn)是沒有變更,同時滿足下面兩種情況我們就認(rèn)定服務(wù)是無變更的:

  1. 代碼無變更:根據(jù)發(fā)布系統(tǒng)中的發(fā)布記錄,找到一段時間內(nèi)沒有代碼發(fā)布、或發(fā)布次數(shù)很少的服務(wù)
  2. 配置無變更:根據(jù)分布式配置中心的變更記錄,找到一段時間內(nèi)沒有配置發(fā)布、或發(fā)布次數(shù)很少的服務(wù)

已下線

服務(wù)是否已下線 很好判斷,只需要取一下當(dāng)前全部服務(wù)的狀態(tài),篩選出下線的服務(wù)。為了更精準(zhǔn),可以取兩個時間點(diǎn)的狀態(tài),這兩個時間點(diǎn)要有一定的跨度,當(dāng)兩個時間點(diǎn)服務(wù)都是下線狀態(tài),則判定服務(wù) “已下線”。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

3、刪得好

找到了全部可能可以被刪除的服務(wù)后,接下來就是刪服務(wù)了,刪服務(wù)要保證三個點(diǎn):

  • 刪得準(zhǔn):禁止誤刪
  • 刪得全:刪除服務(wù)時,要把服務(wù)相關(guān)資源都清理掉,比如服務(wù)的域名、機(jī)器等
  • 刪得快:刪除服務(wù)的效率要高

為此,我們制定了服務(wù)刪除標(biāo)準(zhǔn)流程,搭建了 “應(yīng)用瘦身平臺”。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

在流程中增加人工確認(rèn)步驟,保證 “刪得準(zhǔn)”;整理服務(wù)刪除所牽扯的相關(guān)資源清理過程,沉淀到瘦身平臺中,通過設(shè)計(jì)服務(wù)刪除全流程 與 自動化刪除能力,保證 “刪得全” 和 “刪得快”。

平臺的關(guān)鍵設(shè)計(jì)點(diǎn)是服務(wù)刪除的流程標(biāo)準(zhǔn)化,我們將服務(wù)刪除分為了四個周期、十個步驟,全流程如下圖:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

三、代碼精簡實(shí)戰(zhàn)

1、可精簡代碼特征分析

精簡代碼的思路和精簡服務(wù)一樣,從已知的、具體的操作入手,分析出可精簡代碼的通用特征,然后基于特征、利用工具批量找到可精簡代碼全集,最后執(zhí)行精簡代碼的操作。

常見的可精簡代碼方法有三個:

  • 靜態(tài)代碼分析,刪掉未被調(diào)用的方法。在 IDEA 里對這類方法是直接能提示出來的
  • 重構(gòu)代碼,包括簡化代碼寫法、精簡邏輯、減少重復(fù)代碼等
  • 想辦法找出 長期沒有線上流量的代碼,這些代碼大概率是能被刪除的

這三個手段都能精簡掉代碼,但效果并不一樣,因此我抽象了兩個指標(biāo),從 ”量大“ 和 ”通用性“ 上來評估每個手段的優(yōu)劣。為什么是這兩個指標(biāo),意義何在?“量大” 決定了能刪除的代碼數(shù)量,“通用性” 影響的是能否自動化進(jìn)行,決定了刪除代碼的效率,最終會先選擇同時具備這倆指標(biāo)的手段,這樣能最快的刪除最多的代碼,具體的評估數(shù)據(jù)如下圖:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

通過對已有代碼的全量度量,我們發(fā)現(xiàn) “靜態(tài)未被調(diào)用的方法” 占比是很少的,因此量不大

重構(gòu)是件復(fù)雜的事情,有時涉及到架構(gòu)重構(gòu),牽扯多個微服務(wù),需要對業(yè)務(wù)很熟悉,所以只能由業(yè)務(wù)線同學(xué)自行推進(jìn),難以找到通用的手段 自動完成重構(gòu);重構(gòu)能減少的代碼行數(shù)是很多的,因此這也是達(dá)到最終目標(biāo)的抓手之一

經(jīng)過實(shí)際的代碼分析,發(fā)現(xiàn)沒有流量經(jīng)過的代碼是很多的,尤其對于歷史悠久的系統(tǒng),以及活動類的系統(tǒng),業(yè)務(wù)沒了但代碼通常不會刪掉,這就產(chǎn)生了大量無流量的廢棄代碼。對于如何找到這些無流量的代碼,并進(jìn)行刪除,都具有通用性,因此作為工具平臺組,我們在這個方面做了大量工作,也是分為 “找得到” 和 “刪得好”,下面依次進(jìn)行介紹

2、“找得到” 方案選型

常規(guī)方案

需要通過技術(shù)手段找到?jīng)]有線上流量的代碼,比較容易想到的方案有兩個:

1、AOP

利用 Java 中的 AOP 技術(shù),動態(tài)增強(qiáng)每個方法,在其中增加訪問計(jì)數(shù)邏輯,將源碼中的方法全集減去有計(jì)數(shù)的方法,得到?jīng)]有線上流量的方法,示例代碼如下:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

2、Agent 字節(jié)碼插樁:

通過自實(shí)現(xiàn) Agent,對源碼進(jìn)行字節(jié)碼插樁,增加記錄訪問日志的邏輯,并在 JVM 啟動時設(shè)置 -javaagent 參數(shù)來加載 Agent,剩下 “計(jì)算沒有線上流量的方法” 和 AOP 方案類似,不再贅述。流程圖如下:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

上面兩個方案比較容易想到,除此之外,我們找到了第 3 個方案,基于 SA 工具

3、SA 方案

先不著急介紹 SA 是什么,而從我們熟悉的入手,一步步推理、引出 SA。

在 JVM 中,Java 代碼可以通過解釋執(zhí)行(Interpreted Mode)和編譯執(zhí)行(Compiled Mode)兩種方式來運(yùn)行,默認(rèn)情況下使用混合模式(Mixed Mode)來運(yùn)行應(yīng)用程序,即 將解釋執(zhí)行和編譯執(zhí)行相結(jié)合。JVM 在應(yīng)用程序啟動時會先進(jìn)行解釋執(zhí)行,同時使用即時編譯器來編譯熱點(diǎn)代碼。當(dāng)熱點(diǎn)代碼被編譯執(zhí)行后,就可以使用本地代碼來代替解釋執(zhí)行,從而提高應(yīng)用程序的性能。如果有新的熱點(diǎn)代碼出現(xiàn),即時編譯器會對其進(jìn)行編譯執(zhí)行。

那么 JVM 如何判斷出熱點(diǎn)代碼?猜測一下,有一個方法粒度的計(jì)數(shù)器,統(tǒng)計(jì)了方法執(zhí)行次數(shù),當(dāng)這個次數(shù)超過閾值時觸發(fā)編譯。

通過查看 hotspot 源碼完成了驗(yàn)證,在 Method 類中確實(shí)有方法計(jì)數(shù)相關(guān)的字段。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

有辦法通過 java 代碼讀取到 JVM 中方法計(jì)數(shù)相關(guān)的字段嗎?我們找到了一個工具 Serviceability Agent,這是 JVM 開發(fā)者為了調(diào)試 JVM 而制作的工具 Agent,主要功能是暴露運(yùn)行時 JVM 中的 Java 對象和數(shù)據(jù)結(jié)構(gòu),因此它能把 JVM 中 Method 對象的全部字段都暴露出來,使用 Java 代碼進(jìn)行讀取。

SA 官方介紹地址:https://openjdk.org/groups/hotspot/docs/Serviceability.html

SA 并不高深,常見的 JVM 工具 如 jstack、jmap,底層也是用了 SA

方案選型

對于上面三個方案的選型,從三個維度進(jìn)行評估:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

對于方法執(zhí)行的計(jì)數(shù),SA 方案利用 JVM 已有機(jī)制就記下來了,因此該方案可以達(dá)到 對業(yè)務(wù)服務(wù)沒有任何性能損耗的效果,零風(fēng)險。并且拿到的直接是方法執(zhí)行次數(shù),不需要額外的計(jì)算和聚合,實(shí)現(xiàn)復(fù)雜度低,因此最終選用了 SA 方案。

從結(jié)果上看,SA 方案確實(shí)非常好,最大優(yōu)點(diǎn)就是零風(fēng)險,整個項(xiàng)目推進(jìn)過程中,沒有產(chǎn)生過任何故障。

3、SA 方案詳細(xì)設(shè)計(jì)

本節(jié)將對 SA 方案中的幾個關(guān)鍵設(shè)計(jì)點(diǎn)進(jìn)行詳細(xì)介紹。

性能無損跑數(shù)

使用 SA 對運(yùn)行中的 JVM 進(jìn)行方法計(jì)數(shù)探測,這個過程稱之為 “跑數(shù)”。直接使用 SA 觀測線上的 JVM 是會對性能有影響的,因?yàn)樵谔綔y期間,用戶線程處于 STW 狀態(tài),內(nèi)存占用也會適當(dāng)增加,想要做到完全的性能無損跑數(shù),需要利用好服務(wù)上下線,具體過程如下圖:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

首先隨機(jī)挑選一個線上的實(shí)例,進(jìn)行實(shí)例下線,下線只會讓線上流量不再打到該實(shí)例上,JVM 還是跑著的。然后對下線后實(shí)例,使用 SA 進(jìn)行跑數(shù),完成跑數(shù)后等待幾分鐘,對實(shí)例進(jìn)行上線,這樣就不會因?yàn)榕軘?shù),而對線上流量有性能影響了。

跑數(shù)有三個避坑點(diǎn)需要注意:

SA 跑數(shù)時會消耗一定內(nèi)存,因此在跑數(shù)前 JVM 必須留有足夠的剩余內(nèi)存(通常 大于 500M),這樣才能成功跑出數(shù),否則 JVM 會發(fā)生 OOM 甚至長時間卡住的問題

服務(wù)必須是多實(shí)例的,對于單實(shí)例服務(wù)直接跑數(shù)會導(dǎo)致服務(wù)全部下線,影響業(yè)務(wù)。這種情況建議擴(kuò)容到多個實(shí)例,或者放棄跑數(shù)

有時 prod 類型的環(huán)境會有多個,它們使用的是同一份代碼,此時需要對每個環(huán)境都挑實(shí)例進(jìn)行跑數(shù)

跑數(shù)代碼實(shí)現(xiàn)

SA 就是一個 jar 包,提供了觀測 JVM 的 API,本節(jié)主要介紹跑數(shù)的代碼實(shí)現(xiàn)。

JVM 對于解釋執(zhí)行和編譯執(zhí)行,方法的結(jié)構(gòu)是不一樣的,因此需要區(qū)分處理,代碼如下:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

代碼中的 API 運(yùn)用了觀察者模式,其中 InvocationCounterVisitor 和 CompiledMethodVisitor 是我們自定義的觀察者,兩者的邏輯類似:

  1. 實(shí)現(xiàn)觀察者的接口,實(shí)現(xiàn)核心的觀察(visit)方法
  2. 通過方法入?yún)?,獲取到 Method 對象
  3. 從 Method 對象中取出方法計(jì)數(shù)器值,保存到結(jié)果集中

核心代碼如下圖:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

計(jì)算可精簡方法集

跑一次數(shù),能探測到 “從 JVM 啟動到跑數(shù)時刻” 這段時間內(nèi),每個方法的執(zhí)行次數(shù)。跑完數(shù)之后,得到了一個方法集合,包含方法標(biāo)識、執(zhí)行次數(shù)等信息:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

但是僅跑一次數(shù)是不夠的,可能在 “從 JVM 啟動到跑數(shù)時刻” 這段時間之后,有方法被第一次執(zhí)行了,此時就會產(chǎn)生遺漏。因此要多次、有時間跨度地跑數(shù),比如每天跑一次,持續(xù)跑一個月,這樣才能獲取到更準(zhǔn)確的數(shù)據(jù),減少遺漏概率。

有了多次跑數(shù)結(jié)果后,現(xiàn)在開始計(jì)算 “可精簡方法集”。首先,要對多次的跑數(shù)結(jié)果取并集,獲得全部 “有流量的方法集”;然后,分析工程源碼,利用開源工具(如 Spoon)獲取 “工程方法全集”;最后,在 “工程方法全集” 中排除掉 “有流量的方法集”,得到最終的 “可精簡方法集”,過程如下圖:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

校準(zhǔn)可精簡方法集

如之前所述,采用了隨機(jī)抽取實(shí)例進(jìn)行跑數(shù)的方式,這就可能產(chǎn)生遺漏問題,例如:某服務(wù)線上實(shí)例個數(shù)為 600 個,有一個定時任務(wù)定向的分發(fā)到指定的一臺機(jī)器上,此時理想情況下,隨機(jī)跑數(shù) 600 次后就會命中一次定時任務(wù)所在的實(shí)例,抓取到定時任務(wù)方法的執(zhí)行記錄。但很有可能,跑了 600 次也沒跑到定時任務(wù)所在的機(jī)器,這就產(chǎn)生了有流量方法集的遺漏,最終導(dǎo)致計(jì)算出來的可精簡方法集有誤判。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

要解決這個問題,第一反應(yīng)是能不能對所有實(shí)例都跑數(shù)?這樣一定能避免遺漏,但根據(jù)實(shí)際經(jīng)驗(yàn),這種方式風(fēng)險過高,試想一下,每天跑一次數(shù),每次都需要把服務(wù)的全部實(shí)例輪著下線、上線一遍,操作之繁瑣、風(fēng)險之大可想而知。

我們最終使用的方案是,對于 SA 跑出來的可精簡方法集,在源碼層面、通過 IDEA 批量加上打點(diǎn)(具體如何增加,下文會介紹),發(fā)布到線上后再跑一段時間,根據(jù)打出的點(diǎn)校準(zhǔn)可精簡方法集,這樣就能確保百分百的準(zhǔn)確性了,在代碼中增加的打點(diǎn)如下圖:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

源碼中加打點(diǎn),不會帶來性能損耗嗎?確實(shí)存在損耗,但基本可以忽略不計(jì),因?yàn)檫@些要加打點(diǎn)的方法,是 SA 長期跑出來的可精簡方法集,代表方法執(zhí)行頻率極低甚至沒有,因此對這些方法增加打點(diǎn),能產(chǎn)生的性能影響極少。

4、整體方案

最后,將上面的點(diǎn)串聯(lián)起來看一下整體的方案,業(yè)務(wù)流程圖如下:

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

  1. 定時任務(wù)平臺,定期觸發(fā)瘦身服務(wù)的跑數(shù)邏輯
  2. 瘦身服務(wù)通過發(fā)布平臺,隨機(jī)下線一臺實(shí)例
  3. 瘦身服務(wù)通過 salt 平臺,下發(fā)跑數(shù)的腳本任務(wù)
  4. 在已下線的實(shí)例上完成跑數(shù),將結(jié)果上報到瘦身服務(wù)
  5. 瘦身服務(wù)將跑數(shù)的原始結(jié)果(每個方法的執(zhí)行次數(shù)等信息)進(jìn)行存庫
  6. 瘦身服務(wù)通過發(fā)布平臺,將下線的實(shí)例進(jìn)行上線
  7. 瘦身服務(wù)克隆源碼,獲取 “工程中方法全集”
  8. 從打點(diǎn)監(jiān)控平臺(watcher)上取到全部瘦身相關(guān)打點(diǎn),轉(zhuǎn)換成有打點(diǎn)的方法集合
  9. 將 SA 跑出的結(jié)果方法集 和 有打點(diǎn)的方法集,從 工程方法全集 中排除,得到最終的 “可精簡方法集”,存庫

5、刪得好

代碼想要刪得好,自動化就少不了。我們設(shè)計(jì)并提供了兩種代碼刪除方式,分為 全自動 和 半自動,以適配不同重要性的業(yè)務(wù)團(tuán)隊(duì)。

全自動

整個代碼刪除過程完全自動化,包含 克隆代碼、創(chuàng)建瘦身分支、刪除可精簡代碼、推送分支 4 個步驟。最后需要開發(fā)同學(xué)進(jìn)行 CR,沒問題后走后續(xù)的驗(yàn)證、發(fā)布過程。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

刪除代碼時有個注意點(diǎn),需要保證刪除后的代碼可正常編譯。如果直接刪除方法簽名和方法體,就可能導(dǎo)致無法分支無法正確編譯,這是不能被接受的。

半自動

在全自動方案中,開發(fā)者只有在最終 CR 時才能看到被刪除的代碼,此時如果有大量需要恢復(fù)的代碼,操作起來會比較麻煩,需要手動回滾。同時,為了讓開發(fā)對刪除的代碼有更強(qiáng)的掌控力,因此設(shè)計(jì)了半自動方案。

半自動方案中,我們開發(fā)了一個 idea 插件,該插件能掃出可精簡的方法集合,提供批量刪除方法、置空方法體、增加打點(diǎn)等功能。開發(fā)者利用該插件,手動、高效地進(jìn)行代碼刪除工作,并且對每個方法都有精確掌控,風(fēng)險更低。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

驗(yàn)證

代碼刪完、人工 CR 確認(rèn)后,仍需要做好測試、驗(yàn)證工作 才能最終發(fā)布到線上,具體包含以下六個步驟:

  1. beta 發(fā)布:先在 beta 環(huán)境進(jìn)行發(fā)布
  2. 自動化測試:自動化測試平臺對精簡后的服務(wù),在 beta 環(huán)境完成自動化測試
  3. 故障演練:故障演練是為了保障強(qiáng)弱依賴沒有問題
  4. 灰度發(fā)布:保險起見,對線上環(huán)境先進(jìn)行灰度發(fā)布,并進(jìn)行觀察
  5. 全量發(fā)布:灰度發(fā)布、觀察沒問題后,進(jìn)行全量發(fā)布
  6. 觀察指標(biāo):全量發(fā)布完成后,需要對各種指標(biāo)觀察一段時間,發(fā)現(xiàn)問題及時回滾

四、最終效果

最終我們成功精簡了 49.87% 的代碼,代碼總量減少超千萬,取得了超預(yù)期的效果,具體如下:

  1. 開發(fā)估時平均降低約 10.9%:在刪除冗余代碼、精簡系統(tǒng)后,每個需求的估時和開發(fā)耗時明顯降低,預(yù)計(jì)每年可以節(jié)省近萬 PD 成本;
  2. 發(fā)布效率提升約 9.5%:代碼大量減少后帶來了更快的發(fā)布速度,從克隆到編譯到運(yùn)行,各階段耗時均有縮短。單次提升看似有限,但實(shí)際每月有幾萬次發(fā)布,綜合收益是很可觀的。

綜上所述,系統(tǒng)瘦身是一項(xiàng)有價值的工作,且實(shí)際價值超出預(yù)期。

狂砍千萬行代碼,零故障!去哪兒網(wǎng)系統(tǒng)瘦身技術(shù)揭秘(去哪兒網(wǎng)功能介紹)

五、未來展望

前面分別從服務(wù)精簡和代碼精簡角度,分別介紹了特征分析、找得到、刪得好、實(shí)施經(jīng)驗(yàn),希望能夠幫助大家將代碼量降下來,降低維護(hù)成本,提升開發(fā)效率。在未來,還有以下五個方面的工作可以繼續(xù)探索和實(shí)施:

  • 代碼行粒度精簡:目前的方案是方法粒度的,更進(jìn)一步可以細(xì)分為行粒度,有利于圈復(fù)雜度的降低
  • 長周期代碼精簡:一段代碼如果很長時間才執(zhí)行一次(如 一年一次),執(zhí)行的時候不在 SA 跑數(shù)期間,就會產(chǎn)生誤判,該方法應(yīng)該保留
  • 依賴精簡:對服務(wù)依賴的 jar 包進(jìn)行精簡,減少依賴數(shù)量,避免依賴冗余、減少不兼容的情況
  • 配置精簡:清理服務(wù)所依賴的配置項(xiàng)
  • 服務(wù)合并:探索服務(wù)合并的通用路徑,完成自動化服務(wù)合并功能

作者:馬陽陽

來源:微信公眾號:Qunar技術(shù)沙龍

出處:https://mp.weixin.qq.com/s/H-QedPzfsT88w0n33g6ZhA

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 舉報,一經(jīng)查實(shí),本站將立刻刪除。