微服務(wù)的理論已經(jīng)夠多迟蜜,今天不妨看一個(gè)實(shí)戰(zhàn)案例涎拉。
寫在前面
基于微服務(wù)或者 SOA 的自動(dòng)化測(cè)試系統(tǒng)每個(gè)公司都有自己的特有的晾咪,我今天就主要介紹一下涎才,我們研發(fā)的一套 mock 測(cè)試系統(tǒng)鞋既。
目前面臨的問題
1、測(cè)試人員面臨的測(cè)試問題
我公司目前用的是基于 Dubbo 的微服務(wù)改造耍铜,服務(wù)之間的調(diào)用鏈路冗長邑闺,每個(gè)服務(wù)又是單獨(dú)的團(tuán)隊(duì)在維護(hù),每個(gè)團(tuán)隊(duì)又在不斷的演進(jìn)和維護(hù)各個(gè)服務(wù)棕兼,那么對(duì)測(cè)試人員將是非常大的挑戰(zhàn)陡舅。
測(cè)試人員每次進(jìn)行功能測(cè)試的時(shí)候,測(cè)試用例每次都需要重新寫一遍伴挚,無法將測(cè)試用例的數(shù)據(jù)沉淀靶衍,尤其是做自動(dòng)化測(cè)試的時(shí)候,測(cè)試人員準(zhǔn)備測(cè)試數(shù)據(jù)就需要很長時(shí)間茎芋,效率非常低颅眶。
目前接口自動(dòng)化測(cè)試框架也多種多樣,testng田弥,junit涛酗,F(xiàn)itnesse 等,但都需要測(cè)試人員具備測(cè)試代碼編寫能力,如果要做好和手工接口測(cè)試一樣效果的自動(dòng)化測(cè)試更是需要大量的代碼堆積煤杀,后期維護(hù)代碼成本非常大眷蜈。因此做成簡單配置用例流,無需編寫測(cè)試代碼的系統(tǒng)是更貼合實(shí)際工作要求沈自。
舉個(gè)例子:拿互聯(lián)網(wǎng)支付系統(tǒng)來說酌儒,某個(gè)團(tuán)隊(duì)新增了支付交易的需求,這時(shí)候要進(jìn)行測(cè)試枯途,測(cè)試人員除了要測(cè)試支付交易需求本身是否正確,同時(shí)也要結(jié)合上下游的服務(wù)整體進(jìn)行回歸測(cè)試酪夷,這時(shí)候開發(fā)人員往往在支付交易系統(tǒng)中采用“硬編碼”的方式對(duì)上下游的系統(tǒng)進(jìn)行“擋板”鸥印,如果測(cè)試人員對(duì)測(cè)試數(shù)據(jù)有所調(diào)整那么“擋板”也要跟著調(diào)整字管,同時(shí)在項(xiàng)目正式上線的時(shí)候歇由,如果開發(fā)人員沒有將“擋板”程序去除干凈萝衩,將面臨嚴(yán)重的線上問題喜滨。
2验游、測(cè)試人員如何驗(yàn)證數(shù)據(jù)
接口返回值
通過肉眼分析比對(duì)接口返回值的內(nèi)容旅挤,判斷業(yè)務(wù)邏輯正確性。
數(shù)據(jù)庫驗(yàn)證
測(cè)試接口的輸入值需要通過手工編寫數(shù)據(jù)庫 SQL 查詢獲取,接口調(diào)用完成后乙埃,需要通過大量的 SQL 驗(yàn)證數(shù)據(jù)庫值的正確性。
日志驗(yàn)證
通過返回值和數(shù)據(jù)庫不能確保代碼走到了預(yù)期的邏輯,只能通過肉眼觀察日志確認(rèn)代碼的實(shí)際運(yùn)行邏輯
測(cè)試報(bào)告
人工記錄用例結(jié)果赃阀,人工編寫報(bào)告,耗時(shí)耗力仓技,難以準(zhǔn)確定位代碼問題
Mock 模擬系統(tǒng)的產(chǎn)生
業(yè)務(wù)系統(tǒng)調(diào)用眾多其他系統(tǒng)完成功能邏輯兆衅,而想要得到其他系統(tǒng)接口的特定輸出地沮,需要做相應(yīng)的運(yùn)營配置嗜浮,增加很多的溝通成本;甚至偶發(fā)性 bug 只能在特定的環(huán)境狀況下復(fù)現(xiàn)摩疑,只能作為不可測(cè)的邏輯危融。
以風(fēng)控系統(tǒng)為例,如果業(yè)務(wù)系統(tǒng)需要測(cè)試某個(gè)商編某個(gè)商品類別下的累積限額雷袋,需要風(fēng)控的同事配合不斷修改限額閾值吉殃,目前的情況是多個(gè)業(yè)務(wù)系統(tǒng)都在接入風(fēng)控,配合測(cè)試的人力成本和時(shí)間成本是很高的楷怒。為此設(shè)計(jì)了擋板模擬系統(tǒng)蛋勺,其功能結(jié)構(gòu)如下:
針對(duì)測(cè)試人員測(cè)試用例數(shù)據(jù)無法沉淀和復(fù)用的問題,我們將采用“用例與日志錨點(diǎn)庫”方案:
用例庫的建立可以實(shí)現(xiàn)對(duì)以往測(cè)試規(guī)則的記錄與復(fù)用鸠删,改變每次回歸測(cè)試都要重復(fù)編寫用例與準(zhǔn)備數(shù)據(jù)的現(xiàn)狀抱完。
日志錨點(diǎn)庫是對(duì)代碼執(zhí)行流程的有效驗(yàn)證,除了可以應(yīng)用在測(cè)試環(huán)境中刃泡,還可基于大數(shù)據(jù)日志中心對(duì)生產(chǎn)代碼的運(yùn)行做日常監(jiān)控乾蛤。
交易與支付系統(tǒng)業(yè)務(wù)邏輯復(fù)雜,靠人腦和文檔記憶功能關(guān)系難免疏漏捅僵,而用例庫和日志錨點(diǎn)庫會(huì)隨著業(yè)務(wù)的變更測(cè)試而隨即維護(hù)家卖,是一部活文檔。
Mock 系統(tǒng)的技術(shù)方案
1庙楚、系統(tǒng)的功能點(diǎn)
說明: 上圖羅列了整個(gè) Mock 測(cè)試系統(tǒng)的功能點(diǎn)有哪些上荡,共分為:配置接口數(shù)據(jù)、創(chuàng)建測(cè)試用例馒闷、創(chuàng)建測(cè)試集酪捡、創(chuàng)建測(cè)試計(jì)劃、執(zhí)行測(cè)試計(jì)劃以及生成測(cè)試報(bào)告等大功能纳账。
配置接口數(shù)據(jù)界面圖
創(chuàng)建測(cè)試用例配置圖
擋板請(qǐng)求路由配置圖
測(cè)試執(zhí)行結(jié)果
2逛薇、系統(tǒng)的功能流程圖
說明:依據(jù)上下文環(huán)境,利用工廠類動(dòng)態(tài)注入 spring 對(duì)遠(yuǎn)程接口的依賴疏虫,保持線上與測(cè)試的代碼一致永罚。在測(cè)試環(huán)境中,通過 mock 系統(tǒng)管理端卧秘,可以隨時(shí)調(diào)整請(qǐng)求的流向呢袱,“指哪打哪”。
說明:執(zhí)行某項(xiàng)測(cè)試用例翅敌, 利用 mock 將被測(cè)試接口與底層依賴接口隔離開來羞福,可以方便的模擬數(shù)據(jù),并監(jiān)控輸入輸出蚯涮。用例執(zhí)行完畢后治专,使用返回?cái)嘌月袅辍QL 查詢、日志標(biāo)記等多種手段驗(yàn)證张峰。
Dubbo 的 Mock 功能
1泪蔫、Dubbo 的 Mock 使用
Dubbo 自帶的 Mock 功能首先是為了做服務(wù)降級(jí),比如某驗(yàn)權(quán)服務(wù)挟炬,當(dāng)服務(wù)提供方全部掛掉后,客戶端不拋出異常嗦哆,而是通過 Mock 數(shù)據(jù)返回授權(quán)失敗谤祖。
我們從官網(wǎng)上舉一個(gè)例子來說明:
我們可以在期望的 reference 標(biāo)簽上加一個(gè) mock="force",就可以將當(dāng)前服務(wù)設(shè)置為 mock老速。但是設(shè)置完 mock 屬性后還沒有結(jié)束粥喜,需要有一個(gè) Mock 類對(duì)應(yīng)我們的服務(wù)接口類。
規(guī)則如下:
接口名 + Mock 后綴橘券,服務(wù)接口調(diào)用失敗 Mock 實(shí)現(xiàn)類额湘,該 Mock 類必須有一個(gè)無參構(gòu)造函數(shù)。
對(duì)應(yīng)到 com.foo.BarService 的話旁舰,則創(chuàng)建 BarServiceMock 類锋华。
經(jīng)過以上設(shè)置后,當(dāng)調(diào)用 BarService 進(jìn)行遠(yuǎn)程調(diào)用的話箭窜,直接請(qǐng)求到 BarServiceMock 類上面進(jìn)行模擬測(cè)試毯焕。
2、Dubbo Mock 的原理解析
在 dubbo 的配置文件中
可以看到如下配置列表:
我們可以看到配置文件中實(shí)際上有五大路由策略:
AvailableCluster: 獲取可用的調(diào)用磺樱。遍歷所有 Invokers 判斷 Invoker.isAvalible, 只要一個(gè)有為 true 直接調(diào)用返回纳猫,不管成不成功。
BroadcastCluster: 廣播調(diào)用竹捉。遍歷所有 Invokers, 逐個(gè)調(diào)用每個(gè)調(diào)用 catch 住異常不影響其他 invoker 調(diào)用芜辕。
FailbackCluster: 失敗自動(dòng)恢復(fù), 對(duì)于 invoker 調(diào)用失敗块差, 后臺(tái)記錄失敗請(qǐng)求侵续,任務(wù)定時(shí)重發(fā), 通常用于通知。
FailfastCluster: 快速失敗憨闰,只發(fā)起一次調(diào)用询兴,失敗立即保錯(cuò),通常用于非冪等性操作起趾。
FailoverCluster: 失敗轉(zhuǎn)移诗舰,當(dāng)出現(xiàn)失敗,重試其它服務(wù)器训裆,通常用于讀操作眶根,但重試會(huì)帶來更長延遲蜀铲。
Dubbo 中默認(rèn)使用的是 FailoverCluster 策略棒拂,而在實(shí)際執(zhí)行的過程中是 FailoverCluster 會(huì)被先被注入到 MockClusterWrapper 中钮孵,過程就是:
MockClusterWrapper 內(nèi)部會(huì)創(chuàng)建一個(gè) MockClusterInvoker 對(duì)象。實(shí)際創(chuàng)建是封裝了 FailoverClusterInvoker 的 MockClusterInvoker吕粹,這樣就成功地在 Invoker 之中植入了 Mock 機(jī)制族扰。
我們來看 MockClusterInvoker 的內(nèi)部實(shí)現(xiàn):
如果在沒有配置之中沒有設(shè)置 mock厌丑,那么直接把方法調(diào)用轉(zhuǎn)發(fā)給實(shí)際的 Invoker(也就是 FailoverClusterInvoker)。
如果配置了強(qiáng)制執(zhí)行 Mock渔呵,比如發(fā)生服務(wù)降級(jí)怒竿,那么直接按照配置執(zhí)行 mock 之后返回。
如果是其它的情況扩氢,比如只是配置的是 mock=fail:return null耕驰,那么就是在正常的調(diào)用出現(xiàn)異常的時(shí)候按照配置執(zhí)行 mock。
3录豺、Dubbo Mock 的適用場景
Dubbo 的 Mock 功能主要是為了做服務(wù)降級(jí)而使用的朦肘,服務(wù)提供方在客戶端執(zhí)行容錯(cuò)邏輯,在出現(xiàn) RpcException(比如網(wǎng)絡(luò)失敗双饥,超時(shí)等) 時(shí)進(jìn)行容錯(cuò)媒抠,然后執(zhí)行降級(jí) Mock 邏輯。自身并不適合做 Mock 測(cè)試系統(tǒng)咏花。
自動(dòng)化 Mock 系統(tǒng)的實(shí)現(xiàn)
1领舰、Mock 系統(tǒng)的簡單用例圖
2、Mock 系統(tǒng)的架構(gòu)圖
為了基于 Dubbo 實(shí)現(xiàn) Mock 功能迟螺,需要對(duì) Dubbo 源碼進(jìn)行一些必要的修改冲秽,通過上面的架構(gòu)圖我們可以看到,實(shí)際上我們正是利用了 Dubbo 的 Filter chain 過濾器鏈這一機(jī)制實(shí)現(xiàn)的矩父,為了方便大家更好的理解锉桑,下面將簡單介紹一下 Dubbo 的 Filter 機(jī)制。
?Dubbo 的 Filter 原理分析
Filter:是一種遞歸的鏈?zhǔn)秸{(diào)用窍株,用來在遠(yuǎn)程調(diào)用真正執(zhí)行的前后加入一些邏輯民轴,跟 aop 的攔截器 servlet 中 filter 概念一樣的。
Filter 接口定義:
Filter 的實(shí)現(xiàn)類需要打上 @Activate 注解, @Activate 的 group 屬性是個(gè) string 數(shù)組球订,我們可以通過這個(gè)屬性來指定這個(gè) filter 是在 consumer, provider 還是兩者情況下激活后裸,所謂激活就是能夠被獲取,組成 filter 鏈冒滩。
關(guān)于 SPI 的詳細(xì)介紹請(qǐng)大家參考我之前寫的另一篇文章:
http://www.reibang.com/p/46aa69643c97
ProtocolFilterWrapper:在服務(wù)的暴露與引用的過程中根據(jù) KEY 是 PROVIDER 還是 CONSUMER 來構(gòu)建服務(wù)提供者與消費(fèi)者的調(diào)用過濾器鏈微驶,F(xiàn)ilter 最終都要被封裝到 Wrapper 中的。
構(gòu)建 filter 鏈,當(dāng)我們獲取激活的 filter 集合后就通過 ProtocolFilterWrapper 類中的 buildInvokerChain 方法來構(gòu)建因苹。
?Mock 流程介紹
注:我們?cè)谥行录恿俗远x的“env=test”這樣的屬性配置用來標(biāo)明當(dāng)前環(huán)境是測(cè)試的還是正式的苟耻,用戶每次通過 Dubbo 請(qǐng)求的遠(yuǎn)程服務(wù)的時(shí)候,都會(huì)首先經(jīng)過我們自定義的 Filter扶檐,我們自定義的 Filter 會(huì)首先判斷當(dāng)前的環(huán)境是 test 還是正式凶杖,如果是 test 的環(huán)境則直接訪問 Mock 配置中心獲取提前配置好的 Mock 數(shù)據(jù)并封裝成用戶定義的 Response 對(duì)象返回。
3款筑、Mock 系統(tǒng)的配置中心
Mock 配置中心就是用戶將 mock 數(shù)據(jù)與應(yīng)用環(huán)境建立關(guān)系的系統(tǒng)智蝠,整個(gè)系統(tǒng)就像一個(gè)工作流引擎:
環(huán)境設(shè)置 ->應(yīng)用名稱設(shè)置 ->擋板規(guī)則設(shè)置 ->Facade 服務(wù)接口設(shè)置 ->方法規(guī)則設(shè)置
環(huán)境設(shè)置
注:如果尚未映射來源 IP 地址到環(huán)境,則點(diǎn)擊環(huán)境列表導(dǎo)航鏈接奈梳,進(jìn)入環(huán)境列表頁面杈湾,點(diǎn)擊添加,輸入源 IP 及環(huán)境名颈嚼,點(diǎn)擊確定按鈕毛秘,實(shí)現(xiàn)源 IP 到所設(shè)環(huán)境的映射饭寺。每個(gè)用戶都可以建立屬于自己的測(cè)試環(huán)境阻课。
應(yīng)用名稱設(shè)置
注:創(chuàng)建所使用系統(tǒng)的應(yīng)用名稱,Mock 配置中心默認(rèn)使用中的名稱作為應(yīng)用名稱艰匙。
擋板規(guī)則
注:每一個(gè)擋板規(guī)則都是由一個(gè)環(huán)境名稱和應(yīng)用名稱組成的唯一擋板限煞,在擋板設(shè)置中選擇環(huán)境名稱和應(yīng)用名稱,并且設(shè)置擋板的有效狀態(tài)员凝。
Facade 規(guī)則
注:每一個(gè) Facade 就是一個(gè) Dubbo 的服務(wù)接口類署驻,在這里將自己的 Facade 名稱與全路徑與擋板名稱對(duì)應(yīng),以標(biāo)識(shí)哪些 Facade 服務(wù)接口類是屬于哪個(gè)擋板的健霹。
方法規(guī)則
注:方法規(guī)則是用來設(shè)置每個(gè) Facade 中的需要 mock 的方法的旺上,可以對(duì)不同的方法設(shè)置方法執(zhí)行時(shí)間、方法拋出的異常等等糖埋。
4宣吱、Mock 系統(tǒng)的其他功能
由于不少應(yīng)用項(xiàng)目開發(fā)完后想對(duì)其進(jìn)行單獨(dú)壓測(cè),而很多時(shí)候應(yīng)用系統(tǒng)和其他業(yè)務(wù)系統(tǒng)形成了依賴關(guān)系瞳别,如果不布署其他應(yīng)用系統(tǒng)則無法完成壓測(cè)征候,為了更好的支持性能測(cè)試組進(jìn)行擋板壓測(cè),Mock 系統(tǒng)支持壓測(cè)功能祟敛,而 Mock 系統(tǒng)自身也可以達(dá)到單臺(tái)服務(wù)器 1000TPS 以上(8C8G)疤坝。
歡迎工作一到五年的Java工程師朋友們加入Java程序員開發(fā): 854393687
群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)馆铁、高性能及分布式跑揉、Jvm性能調(diào)優(yōu)、Spring源碼埠巨,MyBatis畔裕,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來學(xué)習(xí)提升自己衣撬,不要再用"沒有時(shí)間“來掩飾自己思想上的懶惰!趁年輕扮饶,使勁拼具练,給未來的自己一個(gè)交代!