有赞精准测试实践
文|魏士超 on質量保障
一、背景介紹
有贊早期業務跑在一個單體php工程上,隨著業務發展,性能拓展性已經滿足不了需求,為了后續發展,底層開始微服務化,整體轉向dubbo框架。從單體轉向分布式框架,測試也面臨著一系列問題,如下:
對于分布式系統中的絕大部分應用,隨著業務發展,自身應用代碼復雜度會不斷增加,如何準確、全面判定代碼修改影響范圍會越來越重要;
一些領域設計不太合理的業務架構,會發現任一應用接口變動會使多個應用受影響。測試過程中會發現只是自身應用代碼一個修改,會導致對外暴露的接口邏輯發生很大變動,此時測試人員需要判定出這個對外暴露的接口對上層應用到底有多大影響;
業務快速迭代導致測試時間不斷壓縮,全量回歸是一個很困難的事情,那么測試范圍需要開發測試人員根據代碼和業務熟悉程度精確把控,風險容易失控;
基于上述背景,我們研發了精準測試工具,作為應用上線質量的參考維度之一,集成到測試工具平臺上供技術部門所有同事使用。
二、整體方案設計
對于上面的痛點,可以分為三步走;第一步修改過的代碼如何識別,第二步分析出自身應用有哪些接口受到影響;第三步獲取上層業務方受到的影響;設計要點如下:
識別變更的代碼:上線代碼和master代碼采用抽象語法樹分析,去除噪音后,比對方法體即可獲取到新增/修改/刪除的方法;
分析影響的自身應用對外暴露的接口,采用動靜結合。靜態分析采用字節碼分析,同時補充了橋接來解決部分多態問題;動態分析采用了和主流調用鏈技術一致的javaagent來對代碼進行織入,為了防止大量織入導致性能變差,只在qa環境進行織入;
對于應用間鏈路查詢,由于有贊內部很早就有一個調用鏈系統,可以實時查看應用接口之間調用詳情,借助這個系統,使用大數據spark或者MR進行離線任務,匯總處理所有的鏈路信息即可獲取應用間所有鏈路信息,上層業務方影響范圍只要查詢鏈路即可獲得。
PS:對于沒有現成調用鏈的公司可以參考成熟的開源工具skywalking,github地址如下:https://github.com/apache/skywalking
三、重點模塊
重點模塊包含了代碼比對,靜態分析,動態分析,動靜結合,應用間影響分析。
3.1 代碼比對
設計思路:影響分析,首先需要判定哪些方法發生變動。傳統的git/svn會把增加的注釋、空白字符、空行等非業務代碼認為是代碼變動,實際上這類變動對于我們業務來說沒有任何影響;而單純判斷編譯后的class文件,是可以避免這些誤判,但判斷哪些方法發生變動,需要比對方法體的指令,同時還要處理各種內部類的問題,難度不??;為了解決這個問題我們采用了語法樹分析,流程如下:
3.2 靜態分析邏輯
設計思路:對于java代碼,分析字節碼可以發現,調用方法是通過invokestatic,invokespecial,invokeinterface,invokevirtual,invokedynamic這五個指令,掃描每個方法體指令中的invoke指令,獲得應用內部調用鏈中的一系列父子節點。每個應用對外暴露出去的接口都可以認為是一棵內部調用鏈的根節點,從根節點出發遍歷可達的所有節點,那么內部調用鏈即可生成。要點如下:
對于字節碼分析,有很多字節碼操作工具,ASM/bcel/Javassist都可以,使用方法都類似,隨便選擇一個就行;
對于invokedynamic指令,單純按照字節碼指令指向的是一個引導方法(Bootstrap Method),需要判定真正執行的方法,進而獲取真正的調用鏈;
為了加快速度,減少后期處理,需要剔除掉不感興趣的父子節點,比如調用三方包/jdk的API/get方法/set方法;
調用接口指令是invokeinterface方法,但實際上真正執行的是接口的實現類代碼,如果接口只有一個實現類,那么我們就可以判定執行的就是這個實現類,從而可以進行橋接;
匿名內部類編譯過程中會生成一個類似A$1的class文件,根據字節碼文件中的EnclosingMethod字段可以判定上層調用方的類名和方法名,從而可以完成方法和匿名內部類方法的橋接;
動態分析&動靜結合
動態分析:對于代碼中存在的AOP和多態,靜態分析無法很好的解決,采用動態分析將會很好的解決這個問題。使用javaagent對內部方法進行代碼織入,當執行自動化或者功能測試,可以記錄一次請求經過的所有內部方法,這樣形成的內部方法調用鏈將會記錄aop和多態執行的真正的方法,靜態的弱點會得到很大的補充。要點如下:
性能問題:大量織入會導致性能損耗,首先判定當前環境,是否是qa環境,qa環境再織入,不要對線上有影響;
織入范圍:只對com.youzan的包進行織入且排除掉二方包(二方包包名一般也為com.youzan.*),排除掉所有的get/set方法,排除掉private方法(子類重寫不了父類私有方法),排除掉這些會大大加快代碼織入速度,且對分析無影響;
對于每次請求到結束返回,整個調用過程可以看作是不斷入棧出棧的過程,調用一個方法是入棧,方法結束為出棧,當棧為空,即表示請求結束,出入棧的順序反映了代碼的調用邏輯,從而形成內部調用鏈;
動靜結合:動態分析會存在樣本不足,內部調用鏈不能完全反映內部方法調用情況;靜態分析存在多態和AOP的問題,存在孤立的節點,無法串聯起來;為了盡可能分析出受影響的范圍,以及避免動靜分析兩種方式的弊端,采用動靜結合,要點如下:
根據動態分析和靜態分析,分別獲取了一系列內部調用鏈,把這些內部調用鏈的節點打散后重新組合得到包含動態和靜態數據的內部調用鏈;
根據新增/修改的方法名稱和方法入參類型,匹配出包含此方法的內部調用鏈,內部調用鏈根節點就是改動點影響的對外暴露接口;
3.3 應用間影響分析
應用間鏈路采集采用的是sdk+javaagent,整體方案類比skywalking,可以參考skywalking進行二次開發。此處主要介紹離線分析思路,應用間調用鏈的數據都是每個應用分批上報,一次請求在各個節點上報的都會包含最上層調用方接口、上層調用方接口以及本接口信息,匯總去重后將反映出整體應用間調用詳情。(PS:由于一些異常情況,實時上報的鏈路數據不一定完整,故離線統計入庫之前需要判定是否為一棵完整的調用關系樹)
3.4 效果
應用內影響的接口效果如下,主要包含了匯總信息/比對頁面/影響點對應的接口:
一個接口可能被多個調用方調用,對于開發和測試人員一般最關注的是接口直屬調用方和入口調用方以及整體的拓撲圖,如下所示:
某些情況下,單條鏈路調用鏈詳情也需要展示出來:
3.5 不足
對于新增代碼的影響面大部分都是依靠字節碼分析,而字節碼分析在多態和AOP方面存在天然短板,影響面會有所丟失
應用內鏈路跟蹤存在大規模代碼織入,對性能和內存資源會造成一定損耗,對于代碼量很大的工程,損耗尤其嚴重;
對于大規模代碼重構或者底層公用方法的變動,影響面分析會覆蓋很多接口,此時依然需要人工評估是否可以縮小測試范圍;
目前應用內代碼分析只支持java語言,缺少其他語言的范圍評估。
-The End-
Vol.217
總結