攜程程式碼分析平臺實現精準測試與應用瘦身

陶然陶然發表於2024-01-17

   一、引言

  1.1 背景

  微服務架構下,產研分工精細,需求迭代頻繁,隨著需求的不斷迭代,應用數、程式碼量及測試用例越積越多;需求迭代(尤其是有新人加入)的過程中,產品經理需要透過開發瞭解現狀和歷史邏輯,開發人員翻閱歷史程式碼花費的時間和精力越來越大,測試人員上線前需要回歸的用例也越來越多,嚴重影響了需求迭代的效率。

  1.2 現狀分析

  目前攜程旅遊BG的後端開發人均應用數超過4個,人均維護的程式碼行近20萬行;每月平均需求迭代的釋出超過2千次,其中核心應用數佔比及其釋出次數佔比都超過8成。

  為了提高需求迭代的效率,旅遊技術團隊設計開發程式碼分析平臺,對應用的現狀(主要是原始碼和測試用例)進行綜合分析發現:生產應用中高達三分之一的程式碼屬於dead程式碼(沒有被引用,也沒有任何生產流量),嚴重影響開發效率;日常迭代中68%的自動化迴歸用例與當前迭代無關,但是為了保障上線質量,測試人員需要對每條失敗用例進行分析排查,不僅影響當前的交付進度,而且隨著需求的快速迭代,自動化測試的可持續性堪憂。

   二、程式碼分析規劃

  本文主要基於後端Java應用介紹如何實現程式碼分析平臺化,並藉助平臺工具實現精準測試和應用瘦身。平臺可以幫助開發人員識別無效程式碼,在短時間內以最小的風險完成應用瘦身,極大的提高研發的效率;同時透過平臺的用例知識庫進行精準測試,在需求迭代過程中只執行本次改動相關的用例,極大的提高自動化迴歸的效率和可持續性。如下圖:  

圖1 程式碼分析與精準測試、應用瘦身

  2.1 分析應用現狀

  透過對應用系統綜合分析,形成知識庫。分析的物件包括原始碼(不含第三方引用)、對外提供的服務(包括api、任務以及訊息)、自動化用例、日常迭代變更以及方法維度的生產流量等。知識庫包含應用基本資訊、統計資訊(例如程式碼規模、方法規模、用例規模)、方法鏈路資訊、用例鏈路等。

  2.2 工具化及流程閉環

  利用分析得到的知識庫,針對特定場景進行工具化和流程閉環,輔助應用治理。

  例如精準測試場景,平臺可以與釋出流程結合起來,開發提測後自動識別變更內容,並智慧推薦自動化用例並執行,將執行結果實時同步給開發和測試人員,實現變更→釋出→用例推薦、執行、反饋→修復變更的閉環。

  應用瘦身場景,從開發角度來看,平臺需提供多視角的輔助分析工具,幫助開發人員確定方法/類是否可以安全的刪除;從管理角度來看,平臺需劃定應用治理前的基線,並將無效程式碼比例作為應用長期治理物件,從而實時評估下屬治理的進度和效果,形成治理過程的閉環。

   三、程式碼分析原理

  程式碼分析的基本單元是方法,主體是應用的整個生命週期,從應用的程式碼倉庫建立以及研發完成程式碼開發,到測試釋出,再到生產執行,我們對不同階段方法的關聯資訊進行分析,最終得到一個完整的知識庫,分析流程及定義如下圖:  

圖2 程式碼分析原理

  3.1 靜態分析

  透過原始碼解析工具解析出所有的方法宣告及呼叫關係。

  針對Java語言常見的解析工具及原理如下:  

  推薦使用java-callgraph2,理由是java-callgraph2專注於類和方法之間呼叫關係的分析,解決了很多常見問題,例如thread、lambda、stream使用場景的呼叫關係缺失等,並且在git上開源,引入原始碼可實現定製化。

  3.2 半動態分析

  透過位元組碼增強技術(如下圖)和用例回放相結合,獲取用例執行的方法鏈,再基於靜態分析進行方法對映,達到半動態的效果。半動態分析工具推薦使用攜程的開源平臺AREX。  

圖3 位元組碼增強技術

  3.3 動態分析

  動態分析是一種程式碼執行時的採集分析,主要方式是收集生產環境方法的執行次數,以確認方法是否有效。目前主要有兩種做法,一種是透過打樁的方式,類似於半動態分析。該方式雖然能夠獲取準確完整的執行時資訊,但考慮到存在程式碼入侵併且可能對生產伺服器效能產生影響,不建議採用這種方法。

  另一種方法是利用Java虛擬機器(JVM)的方法計數器,我們知道JVM採用的是JIT(Just-In-Time)編譯機制,方法執行過程如下圖:  

圖4 JVM-JIT方法編譯執行流程

  這種方式對程式碼無入侵,缺點是訪問JVM方法計數器需要attach虛擬機器程式導致STW(Stop The World),並且方法計數並不代表真正流量,只能反映方法有沒有被執行以及執行的頻度(幸運的是這對我們的場景已經足夠了)。

  綜合考慮,推薦使用第二種方式,另外為了最大程度的降低採集流量期間STW對業務的影響,需要選取最適合採集的例項並提前停止對外服務(叢集部署可以透過例項拉出實現)。

   四、程式碼分析平臺化

  確定了程式碼分析平臺化的目標,並闡述了程式碼分析的基本原理,接下來我們重點剖析平臺化的三個關鍵步驟。

  4.1 步驟一:建立知識庫

  建立知識庫是程式碼分析平臺化的基礎,知識庫可以將需求迭代的流程串連起來,併為後續分析資料(用例、流量等)的落地提供載體。

  4.1.1 獲取應用入口

  應用入口指的是應用對外提供的服務,通常包括對外提供的api、應用定時排程job、訊息(例如qmq)的消費者;應用入口一般都是透過註解標記並自動註冊上線,原理如圖所示,執行時主動向註冊中心註冊例項和服務,被動接受排程和請求。  

圖5 微服務註冊發現流程

  獲取應用入口的最簡單方式是透過程式碼分析根據註解識別。另外,多團隊協作場景的api契約往往採用集中管理模式,應用透過第三方包引入api契約定義,為了避免大量的第三方引用解析,建議透過註冊中心獲取應用入口。

  4.1.2 獲取原始碼

  映象指的是原始碼經過編譯、打包、檢測驗證後得到的容器載入物件,映象是靜態分析的主要輸入。獲取原始碼則是為了得到準確的原始碼統計資訊及變更資訊。

  考慮到開發人員在特定需求迭代過程中會多人協作、多次提交程式碼,因此獲取原始碼及映象的時機建議在叢集部署完成後、對外提供服務前,這樣可以減少不必要分析、節約資源、簡化分析流程以及減少對開發和測試的干擾。

  4.1.3 靜態分析及儲存

  透過靜態分析可以得到方法間的呼叫關係,以及對方法進行標記(api、job、consumer、屬性等)和染色(重寫、繼承、引用、可達等)。靜態分析流程如下圖所示: 

圖6 靜態分析流程

  實體資料建議使用關聯式資料庫儲存,考慮到方法間的呼叫關係複雜多變且層級深,推薦使用圖資料庫儲存方法呼叫關係,不僅檢索的複雜度更低、效能更好,而且能夠比較直觀的反映系統現狀(透過Nebula-Graph儲存並檢索舉例如下圖)。  

圖7 方法呼叫鏈路圖展示

  4.2 步驟二:完善知識庫用例資訊

  在建立知識庫的基礎上,測試作為需求上線前的必備步驟,對測試用例的分析並融入知識庫至關重要。這個步驟我們主要透過用例回放收集用例經過的內部方法和對外api,結合原始碼對比得到的變更方法分析出需求改動直接、間接影響的入口和用例。

  4.2.1 用例回放

  用例回放指的是在用例執行的同時收集程式碼執行資訊。執行用例回放需要滿足兩個前提條件,一是需要有一套自動化用例測試平臺,能夠維護並排程執行自動化用例;二是需要在系統執行時進行打樁,能夠在用例執行的過程中識別用例和方法呼叫資訊,並對外輸出。

  大多數網際網路企業都有自建的自動化測試平臺,這裡不做展開;系統執行時打樁的實現推薦使用開源AREX,不需要修改業務程式碼,僅需系統映象打包時載入代理服務,對系統執行時的影響安全可控。

  4.2.2 分析流程

  透過半動態分析(流程如下圖),獲取用例執行過程中途經的方法鏈路,補充知識庫中透過用例建立起來的方法關聯關係。  

圖8 半動態分析流程

  基於方法呼叫關係在圖中的儲存,用例和方法的關係也採用圖資料庫的儲存,只需要再補充新型別的點(用例)和邊(用例呼叫方法)即可,其表現方式更為直觀(如下圖)。  

圖9 用例方法呼叫鏈圖展示

  4.3 步驟三:完善知識庫流量資訊

  對原始碼、用例的分析是建立在冷資料載入的基礎上,應用程式碼的質量、測試的有效性最終體現在應用對外提供服務的過程中,執行時的資料不可或缺。這個環節我們主要介紹基於動態分析的原理,如何進行生產流量採集,如何將採集資料跟知識庫結合起來,為後續的工具化和流程閉環提供資料支撐。

  4.3.1 生產流量採集

  生產流量採集主要包含兩部分內容,入口流量採集和應用內部方法流量採集。

  入口流量主要指api(job任務/訊息處理)被外部排程的情況。作為日常排查問題和監控的重點,這部分資料通常作為微服務架構的基礎能力,可以直接透過公共基礎服務獲取,這裡不做展開。

  應用內部方法流量採集的原理(動態分析)前面已經介紹過,這裡重點介紹叢集部署的場景下,採集例項選取的三個基本原則。

  首先是保障採集對生產影響最小。主要基於採集需要暫停例項服務的考量,例項拉出前要做叢集服務能力評估,確保服務能力不能下降過多(例如叢集例項數少於3個的情況下不建議自動拉出),拉出後要給未完成的業務執行緒保留一定的處理時間,採集異常或者時間超過一定時長能夠及時中斷恢復拉入。另外針對job類應用建議owner選擇合適時機手工採集。

  其次是確保採集內容有效。方法流量採集本質上是JVM底層方法計數資訊,因此如果例項建立時間過短(例如自動擴容)或者叢集本身只針對特定場景服務(例如操作路由),很多場景都沒有被執行到,採集的意義就不大。

  最後是保障採集過程可持續。隨著業務快速迭代,生產流量是不斷變化的,因此流量採集需要週期性的持續進行。

  4.3.2 分析流程

  透過動態分析(流程如下圖),將方法的流量資訊補充到圖資料庫的點(方法)上,可以動態的反映方法被執行情況,間接的反映方法及自動化用例的有效性。  

圖10 動態分析流程

   五、應用場景

  在知識庫的基礎上,結合精準測試和應用瘦身兩個具體的應用場景,實現工具化和流程閉環,最終完成程式碼分析平臺化建設。

  5.1 精準測試

  5.1.1 用例執行現狀

  自動化用例迴歸作為應用釋出生產前的必經環節,有兩項重要的評估標準:用例執行成功率和新增程式碼行的覆蓋率。在沒有用例推薦之前,一般採用人工選取執行和全量執行兩種方式。

  人工選取的優點是用例有針對性,缺點是不僅效率低而且容易漏選;全量執行雖然可以避免用例選取缺漏,同時可以提高增量程式碼行的覆蓋率,但是用例執行成功率無法保障,往往需要對執行失敗的任務一一排查,花費大量的時間和精力,大多數情況下失敗的用例與迭代的需求並不相關,更令測試同學頭疼的是隨著需求迭代自動化用例在不斷增加,費力度逐漸升高。

  5.1.2 用例推薦

  基於程式碼分析平臺,應用生產釋出前,可以透過對原始碼進行對比分析獲取變更的具體方法,獲取變更通常需要程式碼版本管理工具和對比元件,目前網際網路企業應用比較廣泛的程式碼倉庫管理工具是git,對比元件推薦使用code diff。

  方法宣告不變的變更(例如修改、刪除),知識庫中已經收集了方法呼叫鏈、用例方法鏈,並且對方法進行了入口標記和染色,我們可以準確的識別其關聯的入口及用例;方法宣告變化的變更(例如新增、增加入參),我們利用實時靜態分析可以透過呼叫鏈追溯到影響的入口,透過入口找到關聯的用例。用例推薦功能如下圖,每次只執行程式碼變更相關的用例。  

圖11 用例推薦流程

  5.1.3 流程閉環

  解決了如何準確的推薦用例,接下里需要結合需求迭代的基本流程和應用釋出流程將程式碼變更、用例推薦、用例執行以及結果反饋串連起來(如下圖),實現流程的閉環,才能更好的發揮程式碼分析平臺的作用,最終提高需求迭代效率和質量。  

圖12 精準測試流程閉環

  5.1.4 度量方法及效果

  前面提到自動化用例執行效果的評估標準主要是用例執行成功率和增量行覆蓋率,成功率主要靠用例本身的質量來保障,增量行覆蓋率可以作為衡量精準測試準確性的度量方法之一。理論上精準測試的增量行覆蓋率只要不能接近全量執行的增量行覆蓋率,就說明推薦用例存在缺失。

  經過驗證,目前我們精準測試的增量行覆蓋率可以達到全量執行的99.2%(偏差主要來自於環境依賴),全面推廣後平均減少了68%與當次需求迭代無關的用例迴歸。隨著自動化用例的不斷增加,精準推薦已經成為自動化迴歸不可或缺的一環。

  5.2 應用瘦身

  5.2.1 應用程式碼現狀

  應用經歷一段時間的需求迭代之後無效程式碼就會開始累積,究其原因主要有三個方面,一是需求變更後部分分支不會再被執行到,由於種種原因沒有來得及重構;二是專案上線初期的臨時檢測對比的分支,專案上線後沒有及時清除;三是上游場景變化導致下游部分場景不再被執行,但下游自身又無法識別。

  過高比例的無效程式碼不僅影響系統的編譯速度,而且嚴重影響開發的效率,尤其是對於新接手的同學,需要了解大量的程式碼歷史背景才能熟悉系統現有的邏輯,而且程式碼量越大邏輯越複雜,修改的風險也就越高,即使經驗豐富的工程師也不敢輕言重構。

  5.2.2 工具化

  基於程式碼分析的知識庫資訊,以方法為基本單位進行引用、入口以及生產流量的多維度分析(如下圖),並提供應用分析工具,輔助開發人員快速的識別無效程式碼,實現應用瘦身。  

圖13 方法可達分析

  5.2.3 流程閉環

  從研發的角度看,刪除程式碼存在一定的風險,如果能夠便捷的透過工具獲取程式碼的生產流量情況以及外部依賴情況,將極大的降低這種風險,增強其應用瘦身(包括程式碼重構和刪除)的信心;從團隊管理的角度來看,如何衡量治理的效果、把控治理過程的風險以及長遠地評估無效程式碼的合理範圍也同樣重要,因此平臺從研發和管理兩個角度實現了閉環(如下圖)。  

圖14 應用瘦身流程閉環

  5.2.4 試點效果及經驗

  完成工具化和流程閉環,我們拿團隊內部10%的應用(100+)經過1個月的試點,輕鬆實現了零故障刪除百萬級程式碼行的目標。其中15%的大規模應用(程式碼行大於20萬行)經過瘦身後系統映象的生成時長從幾十分鐘級降至到幾分鐘(耗時包括編譯、UT執行、合規掃描等)。程式碼鏈路複雜度也明顯降低,如下圖所示。  

圖15 應用瘦身前後方法鏈對比

  經過試點,我們總結了應用瘦身需要嚴格遵守的三個原則,一是瘦身工具只是輔助,程式碼刪除一定要由對應用背景有一定了解的同學進行;二是刪除程式碼一定要經過合作伙伴或leader的review;三是應用瘦身是一個長期治理的過程,不能急於一時或一次了事。

   六、總結

  本文主要基於微服務架構下為了提高需求迭代效率,透過程式碼分析形成知識庫,針對精準推薦和應用瘦身兩個場景進行工具化和流程閉環,初步完成程式碼分析平臺化建設。

  目前已支援的場景還需要進一步細化(例如應用瘦身可以支援本地開發工具的外掛化),結合當前的知識庫,後續還可以支援更多的場景(例如工程複雜度、用例質量等等)。本文只是拋磚引玉,為應用治理提供了一種全新的思路,程式碼分析平臺化是一個長期持續的工程,需要走的路還很長。

來自 “ 攜程技術 ”, 原文作者:Kevin;原文連結:https://server.it168.com/a2024/0117/6837/000006837331.shtml,如有侵權,請聯絡管理員刪除。

相關文章