您好,登錄后才能下訂單哦!
標(biāo)簽:microServices autoTest mock unitTest testTrace
從 SOA 架構(gòu)到現(xiàn)在大行其道的微服務(wù)架構(gòu),系統(tǒng)越拆越小,整體架構(gòu)的復(fù)雜度也是直線上升,我們一直老生常談的微服務(wù)架構(gòu)下的技術(shù)難點(diǎn)及解決方案也日漸成熟(包括典型的數(shù)據(jù)一致性,系統(tǒng)調(diào)用帶來的一致性問題,還是跨節(jié)點(diǎn)跨機(jī)房復(fù)制帶來的一致性問題都有了很多解決方案),但是有一個(gè)環(huán)節(jié)我們明顯忽略了。
在現(xiàn)在的微服務(wù)架構(gòu)趨勢(shì)下,微服務(wù)在運(yùn)維層面和自動(dòng)化部署方面基本上是比較完善了。從我個(gè)人經(jīng)驗(yàn)來看,上層的開發(fā)、測(cè)試對(duì)微服務(wù)架構(gòu)帶來的巨大變化還在反應(yīng)和學(xué)習(xí)中。
開發(fā)層面討論微服務(wù)的更多是框架、治理、性能等,但是從完整的軟件工程來看我們嚴(yán)重缺失分析、設(shè)計(jì)知識(shí),這也是我們現(xiàn)在的工程師普遍缺乏的技術(shù)。
我們經(jīng)常會(huì)發(fā)現(xiàn)一旦你想重構(gòu)點(diǎn)東西是多么的艱難,就是因?yàn)樵诔跗跇?gòu)造這棟建筑的時(shí)候嚴(yán)重缺失了通盤的分析、設(shè)計(jì),最終導(dǎo)致這個(gè)建筑慢慢僵化最后人見人怕,因?yàn)樗饾u變成一個(gè)怪物。(比如,開發(fā)很少寫 unitTest ,我們總是忽視單元測(cè)試背后產(chǎn)生的軟件工程的價(jià)值。)
我們有沒有發(fā)現(xiàn)一個(gè)現(xiàn)象,在整個(gè)軟件過程里,測(cè)試這個(gè)環(huán)節(jié)容易被忽視。任何一種軟件工程模型都有 QA 環(huán)節(jié),但是這個(gè)環(huán)節(jié)似乎很薄很弱,目前我們絕大多數(shù)工程師、架構(gòu)師都嚴(yán)重低估了這個(gè)環(huán)節(jié)的力量和價(jià)值,還停留在無技術(shù)含量,手動(dòng)功能測(cè)試低級(jí)效率印象里。
這主要是測(cè)試這個(gè)角色整個(gè)技術(shù)體系、工程化能力偏弱,一部分是客觀大環(huán)境問題,還有一部分自身問題,沒有讓自己走出去,多去學(xué)習(xí)整個(gè)工程化的技術(shù),多去了解開發(fā)的技術(shù),生產(chǎn)上的物理架構(gòu),這會(huì)有助于測(cè)試放大自己的聲音。
導(dǎo)致測(cè)試環(huán)節(jié)在國內(nèi)整個(gè)設(shè)計(jì)創(chuàng)新薄弱的原因還有一個(gè)主要原因就是,開發(fā)工程師普遍沒有完整的工程基礎(chǔ)。在國外IT發(fā)達(dá)國家,日本、美國等,一個(gè)合格的開發(fā)工程師、測(cè)試工程師都是邊界模糊的,自己開發(fā)產(chǎn)品自己測(cè)試,這需要切換思維模式,需要同時(shí)具備這兩種能力,但是這才是整個(gè)軟件工程的完整流程。
我們有沒有想過一個(gè)問題,為什么現(xiàn)在大家都在談?wù)?DevOps,而不是 DevTestOps,為什么偏偏跳過測(cè)試這個(gè)環(huán)節(jié),難道開發(fā)的系統(tǒng)需要具備良好的可運(yùn)維性就不需要可測(cè)試性嗎,開發(fā)需要具備運(yùn)維能力,運(yùn)維需要具備開發(fā)能力,為什么測(cè)試環(huán)節(jié)忽略了。
我們對(duì) QA 環(huán)節(jié)的輕視,對(duì)測(cè)試角色的不重視其實(shí)帶來的副作用是非常大的。
微服務(wù)的拆分粒度要比 SOA 細(xì)了很多,從容器化鏡像自動(dòng)部署來衡量,是拆小了之后很方便,但是拆小了之后會(huì)給整個(gè)開發(fā)、測(cè)試環(huán)節(jié)增加很大的復(fù)雜度和效率問題。
在 SOA 時(shí)期,契約驅(qū)動(dòng) 這個(gè)原則在微服務(wù)里也一樣適用,跨部門需求定義好契約你就可以先開發(fā)上線了。但是這個(gè)里面最大的問題就是當(dāng)前系統(tǒng)的部分連調(diào)問題和自動(dòng)化回歸問題,如果是新系統(tǒng)上線還需要做性能壓測(cè),這外部的依賴如何解決。
也許我們會(huì)說,不是應(yīng)該依賴方先ready,然后我們緊接著進(jìn)行測(cè)試、發(fā)布嗎。如果是業(yè)務(wù)、架構(gòu)合理的情況下,這種場(chǎng)景最大的問題就是我們的項(xiàng)目容易被依賴方牽制,這會(huì)帶來很多問題,比如,研發(fā)人員需要切換出來做其他事情,branch 一直掛著,不知道哪天突然來找你說可以對(duì)接了,也許這已經(jīng)過去一個(gè)月或者更久,這種方式一旦養(yǎng)成習(xí)慣性研發(fā)流程就很容易產(chǎn)生線上 BUG 。
還有一種情況也是合理的情況就是平臺(tái)提供方需要調(diào)用業(yè)務(wù)方的接口,這里面有一般調(diào)用的 callback 接口、交易鏈路上的 marketing 接口、配送 routing 接口等。
這里給大家分享我們目前正在進(jìn)行中的 marketing-cloud (營銷云) 規(guī)則引擎 項(xiàng)目。
marketing-cloud 提供了一些營銷類業(yè)務(wù),有 團(tuán)購、優(yōu)惠券、促銷 等,但是我們的業(yè)務(wù)方需要有自己個(gè)性化的營銷活動(dòng)玩法,我們需要在 marketing-cloud 規(guī)則引擎 中抽象出業(yè)務(wù)方營銷活動(dòng)的返回信息,同時(shí)打通個(gè)性化營銷活動(dòng)與公共交易、結(jié)算環(huán)節(jié),形成一個(gè)完整的業(yè)務(wù)流。
這是一個(gè) marketing-cloud 邏輯架構(gòu)圖,跟我們主題相關(guān)的就是 營銷規(guī)則引擎 ,他就是我們這里所說的合理的業(yè)務(wù)場(chǎng)景。
在整個(gè)正向下單過程中,營銷規(guī)則引擎要肩負(fù)起既要提供 marketing-cloud 內(nèi)的共用營銷活動(dòng),還需要橋接外部營銷中心的各類營銷玩法,外部的營銷中心會(huì)有多個(gè),目前我們主要有兩個(gè)。
由于這篇文章不是介紹營銷平臺(tái)怎么設(shè)計(jì),所以這里不打算擴(kuò)展話題。主要是起到拋磚引玉的目的,平臺(tái)型的業(yè)務(wù)會(huì)存在各種各樣的對(duì)外系統(tǒng)依賴的業(yè)務(wù)場(chǎng)景。文章接下來的部分將展開 marketing-cloud 規(guī)則引擎 在打通測(cè)試鏈路上的實(shí)踐。
在開發(fā)階段,我們會(huì)經(jīng)常性的編寫單元測(cè)試來測(cè)試我們的邏輯,在編寫 unitTest 的時(shí)候都需要 mock 周邊的依賴,mock 出來的對(duì)象分為兩種類型,一種是不具有 Assert 邏輯的 stub 樁 對(duì)象,還有一種就是需要支持 Assert 的 mocker 模擬對(duì)象。
但是我們也不需要明顯區(qū)分他們,兩者的區(qū)別不是太明顯,在編碼規(guī)范內(nèi)可能需要區(qū)分。
我們關(guān)心的是如何解決對(duì)象之間的依賴問題,各種 mock 框架其實(shí)提供了很多非常好用的工具,我們可以很輕松的 mock 周邊的依賴。
given(marketingService.mixMarketingActivity(anyObject())).willReturn(stubResponse);
RuleCalculateResponse response = this.ruleCalculatorBiz.ruleCalculate(request);
這里我們 mock 了 marketingService.mixMarketingActivity() 方法。
Java 世界里提供了很多好用的 mock 框架,比較流行好用的框架之一 mockito 可以輕松 mock Service 層的依賴,當(dāng)然除了 mockito 之外還有很多優(yōu)秀的 mock 框架。
這些框架大同小異,編寫 unitTest 最大的問題就是如何重構(gòu)邏輯使之更加便于測(cè)試,也就是代碼是否具備很好的可測(cè)試性,是否已經(jīng)消除了絕大多數(shù) private 方法,private 方法是否有某些指責(zé)是我們沒有捕捉到業(yè)務(wù)概念。
在我們完成了所有的開發(fā),完善的單元測(cè)試保證了我們內(nèi)部的邏輯是沒有問題的(當(dāng)然這里不討論 unitTest 的 case 的設(shè)計(jì)是否完善情況)。
現(xiàn)在我們需要對(duì)接周邊系統(tǒng)開發(fā)進(jìn)行連調(diào)了,這個(gè)周邊系統(tǒng)還是屬于本平臺(tái)之類的其他支撐系統(tǒng)。比如我們的 marketing-cloud 規(guī)則引擎系統(tǒng) 與 下單系統(tǒng) 之間的關(guān)系。在開發(fā)的時(shí)候我們編寫 unitTest 是順利的完成了開發(fā)解決的驗(yàn)證工作,但是現(xiàn)在面對(duì)連調(diào)問題。
系統(tǒng)需要正式的跑起來,但是我們?nèi)狈?duì)外部營銷中心的依賴,我們?cè)趺崔k。其實(shí)我們也需要在連調(diào)階段 mock 外部依賴,只不過這個(gè) mock 的技術(shù)和方法不是通過 unitTest 框架來支持,而是需要我們自己來設(shè)計(jì)我們的整個(gè)服務(wù)的開發(fā)架構(gòu)。
首先要能識(shí)別本次 request 是需要 mock 的,那就需要某種 mock parameter 參數(shù)來提供識(shí)別能力。
我們來看下 marketing-cloud 營銷規(guī)則引擎 在這塊的一個(gè)初步嘗試。
public interface CCMarketingCentralFacade {
CallResponse callMarketingCentral(CallRequest request);
}
public interface ClassMarketingCentralFacade {
CallResponse callMarketingCentral(CallRequest request);
}
營銷規(guī)則引擎使用 RestEasy client api 作為 rest 調(diào)用框架。這兩個(gè) Facade 是營銷平臺(tái)對(duì) CCTalk 、滬江網(wǎng)校 滬江兩大子公司營銷中心發(fā)起調(diào)用的 Facade。
(為了盡量還原我們的工程實(shí)踐干貨同時(shí)需要消除一些敏感信息的情況下,整篇文章所有的代碼實(shí)例,我都刪除了一些不影響閱讀且和本文無關(guān)的代碼,同時(shí)做了一些偽編碼和省略,使代碼更精簡(jiǎn)更便于閱讀。)
在正常邏輯下,我們會(huì)根據(jù)營銷路由 key 來決定調(diào)用哪個(gè)公司的營銷中心接口,但是由于我們?cè)陂_發(fā)這個(gè)項(xiàng)目的時(shí)候暫時(shí)業(yè)務(wù)方還沒有存在的地址讓我們對(duì)接,所以我們自己做了 mock facade,來解決連調(diào)問題。
public class CCMarketingCentralFacadeMocker implements CCMarketingCentralFacade {
@Override
public CallResponse callMarketingCentral(CallRequest request) {
CallResponse response = ...
MarketingResultDto marketingResultDto = ...
marketingResultDto.setTotalDiscount(new BigDecimal("90.19"));
marketingResultDto.setUseTotalDiscount(true);
response.getData().setMarketingResult(marketingResultDto);
return response;
}
}
public class ClassMarketingCentralFacadeMocker implements ClassMarketingCentralFacade {
@Override
public CallResponse callMarketingCentral(CallRequest request) {
CallResponse response = ...
MarketingResultDto marketingResultDto = ...
marketingResultDto.setUseCoupon(true);
marketingResultDto.setTotalDiscount(null);
marketingResultDto.setUseTotalDiscount(false);
List<MarketingProductDiscountDto> discountDtos = ...
request.getMarketingProductTagsParameter().getMarketingTags().forEach(item -> {
MarketingProductDiscountDto discountDto = ...
discountDto.setProductId(item.getProductID());
...
discountDtos.add(discountDto);
});
...
return response;
}
}
我們定義了兩個(gè) mock 類,都是一些測(cè)試數(shù)據(jù),就是為了解決在連調(diào)階段的問題,也就是在 DEV 環(huán)境上的依賴問題。
有了 mock facade 之后就需要 request 定義 mock parameter 參數(shù)了。
public abstract class BaseRequest implements Serializable {
public MockParameter mockParameter;
}
public class MockParameter {
/**
* mock cc 營銷調(diào)用接口
*/
public Boolean mockCCMarketingInterface;
/**
* mock class 營銷調(diào)用接口
*/
public Boolean mockClassMarketingInterface;
/**
* 是否自動(dòng)化測(cè)試 mock
*/
public Boolean useAutoTestMock;
/**
* 測(cè)試mock參數(shù)
*/
public String testMockParam;
}
我們暫且忽略通用型之類的設(shè)計(jì),這里只是我們?cè)谮s項(xiàng)目的情況下做的一個(gè)迭代嘗試,等我們把這整個(gè)流程都跑通了再來考慮重構(gòu)提取框架。
有了輸入?yún)?shù),我們就可以根據(jù)參數(shù)判斷來動(dòng)態(tài)注入 mock facade。
我們繼續(xù)向前推進(jìn),過了連調(diào)階段緊接著就進(jìn)入測(cè)試環(huán)節(jié),現(xiàn)在基本上大多數(shù)互聯(lián)網(wǎng)公司都是自動(dòng)化的測(cè)試,很少在有手動(dòng)的,尤其是后端系統(tǒng)。
那么在 autoTest 階段面臨的一個(gè)問題就是,我們需要一個(gè)公共的 autoTest 地址,這個(gè)測(cè)試地址是不變的,我們?cè)谧詣?dòng)化測(cè)試下 mock 的 facade bean 的地址就是這個(gè)地址,這個(gè)地址輸出的值需要能夠?qū)?yīng)到每次自動(dòng)化腳本執(zhí)行的上下文中。
我們有很多微服務(wù)系統(tǒng)來組成一個(gè)平臺(tái),每個(gè)服務(wù)都有依賴的第三方接口,原來在自動(dòng)化測(cè)試這些服務(wù)的時(shí)候都需要去了解業(yè)務(wù)方系統(tǒng)的接口、DB、前臺(tái)入口等,因?yàn)樵诰帉懽詣?dòng)化腳本的時(shí)候需要同步創(chuàng)建測(cè)試數(shù)據(jù),最后才能 Assert。
這個(gè)跨部門的溝通和協(xié)作效率嚴(yán)重低下,而且人員變動(dòng)、系統(tǒng)變動(dòng)都會(huì)直接影響上線周期,這里絕對(duì)值得創(chuàng)新來解決這個(gè)效率嚴(yán)重阻塞問題。
@Value("${marketing.cloud.business.access.url.mock}")
private String mockUrl;
/**
* 自動(dòng)化測(cè)試 mocker bean
*/
@Bean("CCMarketingCentralFacadeTestMock")
public CCMarketingCentralFacade CCMarketingCentralFacadeTestMock() {
RestClientProxyFactoryBean<CCMarketingCentralFacade> restClientProxyFactoryBean ...
restClientProxyFactoryBean.setBaseUri(this.mockUrl);
...
}
/**
* 自動(dòng)化測(cè)試 mocker bean
*/
@Bean("ClassMarketingCentralFacadeTestMock")
public ClassMarketingCentralFacade ClassMarketingCentralFacadeTestMock() {
RestClientProxyFactoryBean<ClassMarketingCentralFacade> restClientProxyFactoryBean ...
restClientProxyFactoryBean.setBaseUri(this.mockUrl);
...
}
這里的 mockUrl 就是我們抽象出來的統(tǒng)一的 autoTest 地址,在前面的 mock parameter 中有一個(gè) useAutoTestMock Boolean 類型的參數(shù),如果當(dāng)前請(qǐng)求此參數(shù)為 true,我們將動(dòng)態(tài)注入自動(dòng)化測(cè)試 mock bean ,后續(xù)的所有調(diào)用都會(huì)走到 mockUrl 指定的地方。
到目前為止,我們遇到了自動(dòng)化測(cè)試統(tǒng)一的 mock 地址要收口所有微服務(wù)在這方面的需求?,F(xiàn)在最大的問題就是,所有的微服務(wù)對(duì)外依賴的 response 都不相同,自動(dòng)化腳本在執(zhí)行的時(shí)候預(yù)先創(chuàng)建好的 response 要能適配到當(dāng)前測(cè)試的上下文中。
比如,營銷規(guī)則引擎,我們的自動(dòng)化腳本在創(chuàng)建一個(gè)訂單的時(shí)候需要預(yù)先構(gòu)造好當(dāng)前商品(比如,productID:101010),在獲取外部營銷中心提供的活動(dòng)信息和抵扣信息的 response ,最后才能去 Assert 訂單的金額和活動(dòng)信息記錄是否正確,這就是一次 autoTest context 。
有兩種方式來識(shí)別當(dāng)前 autoTest context ,一種是在 case 執(zhí)行的時(shí)候確定商品ID,最后通過商品ID來獲取 mock 的 response 。還有一種就是支持傳遞 autoTest mock 參數(shù)給到 mockUrl 指定的服務(wù),可以使用這個(gè)參數(shù)來識(shí)別當(dāng)前測(cè)試上下文。
一個(gè)測(cè)試 case 可能會(huì)穿過很多微服務(wù),這些所有的依賴服務(wù)可能都需要預(yù)設(shè) mock response,這基本上是一勞永逸的。
所以,我們抽象出了 autoTest Mock Gateway(自動(dòng)化測(cè)試mock網(wǎng)關(guān)服務(wù)) ,在整個(gè)自動(dòng)化測(cè)試環(huán)節(jié)還有很多需要支持的工作,服務(wù)之間的鑒權(quán),鑒權(quán) key 的 mock,加解密,加解密 key 的 mock,自動(dòng)化測(cè)試 case 交替并行執(zhí)行等。
作為工程師的我們都希望用系統(tǒng)化、工程化的方式來解決整體問題,而不是個(gè)別點(diǎn)狀問題。有了這個(gè) mock gateway 我們可以做很多事情,也可以普惠所有需要的其他部門。
在一次 autoTest context 里構(gòu)造好 mock response,然后通過 mock parameter 來動(dòng)態(tài)識(shí)別具體的來源服務(wù)進(jìn)行路由、鑒權(quán)、加解密等操作。
MockGateway 是一個(gè)支點(diǎn),我相信這個(gè)支點(diǎn)可以撬動(dòng)很多測(cè)試空間和創(chuàng)新能力。
接下來我們將展示在 marketing-cloud 營銷規(guī)則引擎 中的初步嘗試。
自動(dòng)化腳本在每跑一個(gè) case 的時(shí)候會(huì)創(chuàng)建當(dāng)前 case 對(duì)應(yīng)的 autoTestContext,這里面都是一些 meta data,用來表示這個(gè) case 中所有涉及到的微服務(wù)系統(tǒng)哪些是需要走 mock gateway 的。
在 mockGateway 中所有的配置都是有一個(gè) autoTestContext 所對(duì)應(yīng),如果沒有 autoTestContext 說明是所有 case 共用。
要想打通整個(gè)微服務(wù)架構(gòu)中的所有通道,就需要在標(biāo)準(zhǔn) request contract 定義 mockParameter ,這是這一切的前提。
服務(wù)與服務(wù)之間調(diào)用走標(biāo)準(zhǔn)微服務(wù) request contract,服務(wù)與外部系統(tǒng)的依賴可以選擇走 HTTP Header,也可以選擇走標(biāo)準(zhǔn) request ,就要看我們的整個(gè)服務(wù)框架是否已經(jīng)覆蓋所有的產(chǎn)線及一些遺留系統(tǒng)的問題。
public abstract class BaseRequest implements Serializable {
public MockParameter mockParameter;
}
BaseRequest 是所有 request 的基類,這樣才能保證所有的請(qǐng)求能夠正常的傳遞。
整個(gè)系統(tǒng)的開發(fā)架構(gòu)分層依賴是:facade->biz->service,基本的所有核心邏輯都是在 service 中,請(qǐng)求的 request dto 最多不能越界到 service 層,按照規(guī)范講 request dto 頂多滯留在 biz 層,但是在互聯(lián)網(wǎng)的世界中一些都是可以快速迭代的,并不是多么硬性規(guī)定,及時(shí)重構(gòu)是償還技術(shù)債務(wù)的主要方法。
前面我們已經(jīng)講過,我們采用的 RPC 框架是 RestEasy + RestEasy client ,我們先來看下入口的地方。
@Component
@Path("v1/calculator/")
public class RuleCalculatorFacadeImpl extends BaseFacade implements RuleCalculatorFacade {
@MockFacade(Setting = MockFacade.SETTING_REQUEST_MOCK_PARAMETER)
public RuleCalculateResponse ruleCalculate(RuleCalculateRequest request) {
...
}
}
再看下 service 對(duì)象。
@Component
public class MarketingServiceImpl extends MarketingBaseService implements MarketingService {
@MockFacade(Setting = MockFacade.SETTING_FACADE_MOCK_BEAN)
public MarketingResult onlyExtendMarketingActivity(Marketing..Parameter tagsParameter) {
...
}
我們重點(diǎn)看下 @MockFacade annotation 聲明。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MockFacade {
String SETTING_REQUEST_MOCK_PARAMETER = "setting_request_mock_parameter";
String SETTING_FACADE_MOCK_BEAN = "setting_facade_mock_bean";
String Setting();
}
通過這個(gè) annotation 我們的主要目的就是將 mockParameter 放到 ThreadLocal 中去和請(qǐng)求處理完時(shí)的清理工作。還有一個(gè)功能就是 service 層的 mock bean 處理。
@Aspect
@Component
@Slf4j
public class MockMarketingFacadeInterceptor {
@Before("@annotation(mockFacade)")
public void beforeMethod(JoinPoint joinPoint, MockFacade mockFacade) {
String settingName = mockFacade.Setting();
if (MockFacade.SETTING_REQUEST_MOCK_PARAMETER.equals(settingName)) {
Object[] args = joinPoint.getArgs();
if (args == null) return;
List<Object> argList = Arrays.asList(args);
argList.forEach(item -> {
if (item instanceof BaseRequest) {
BaseRequest request = (BaseRequest) item;
if (request.getMockParameter() != null) {
MarketingBaseService.mockParameterThreadLocal.set(request.getMockParameter());
log.info("----setting mock parameter:{}", JSON.toJSONString(request.getMockParameter()));
}
}
});
} else if (MockFacade.SETTING_FACADE_MOCK_BEAN.equals(settingName)) {
MarketingBaseService marketingBaseService = (MarketingBaseService) joinPoint.getThis();
marketingBaseService.mockBean();
log.info("----setting mock bean.");
}
}
@After("@annotation(mockFacade)")
public void afterMethod(JoinPoint joinpoint, MockFacade mockFacade) {
if (MockFacade.SETTING_FACADE_MOCK_BEAN.equals(mockFacade.Setting())) {
MarketingBaseService marketingBaseService = (MarketingBaseService) joinpoint.getThis();
marketingBaseService.mockRemove();
log.info("----remove mock bean.");
}
if (MockFacade.SETTING_REQUEST_MOCK_PARAMETER.equals(mockFacade.Setting())) {
MarketingBaseService.mockParameterThreadLocal.remove();
log.info("----remove ThreadLocal. ThreadLocal get {}", MarketingBaseService.mockParameterThreadLocal.get());
}
}
}
這些邏輯完全基于一個(gè)約定,就是 MarketingBaseService,不具有通用型,只是在逐步的重構(gòu)和提取中,最終會(huì)是一個(gè) plugin 框架。
public abstract class MarketingBaseService extends BaseService {
protected ClassMarketingCentralFacade classMarketingCentralFacade;
protected CCMarketingCentralFacade ccMarketingCentralFacade;
public static ThreadLocal<MockParameter> mockParameterThreadLocal = new ThreadLocal<>();
public void mockBean() {
MockParameter mockParameter = mockParameterThreadLocal.get();
if (mockParameter != null && mockParameter.mockClassMarketingInterface) {
if (mockParameter.useAutoTestingMock) {
this.setClassMarketingCentralFacade(SpringContextHolder.getBean("ClassMarketingCentralFacadeTestMock", ClassMarketingCentralFacade.class));
} else {
this.setClassMarketingCentralFacade(SpringContextHolder.getBean("ClassMarketingCentralFacadeMocker", ClassMarketingCentralFacadeMocker.class));
}
} else {
this.setClassMarketingCentralFacade(SpringContextHolder.getBean("ClassMarketingCentralFacade", ClassMarketingCentralFacade.class));
}
if (mockParameter != null && mockParameter.mockCCMarketingInterface) {
if (mockParameter.useAutoTestingMock) {
this.setCcMarketingCentralFacade(SpringContextHolder.getBean("CCMarketingCentralFacadeTestMock", CCMarketingCentralFacade.class));
} else {
this.setCcMarketingCentralFacade(SpringContextHolder.getBean("CCMarketingCentralFacadeMocker", CCMarketingCentralFacadeMocker.class));
}
} else {
this.setCcMarketingCentralFacade(SpringContextHolder.getBean("CCMarketingCentralFacade", CCMarketingCentralFacade.class));
}
}
public void mockRemove() {
mockParameterThreadLocal.remove();
}
}
我們可以順利的將 request 中的 mockParameter 放到 ThreadLocal 中,可以動(dòng)態(tài)的通過 AOP 的方式來注入相應(yīng)的 mockerBean。
現(xiàn)在我們還要處理的就是對(duì) mockGateway 的調(diào)用將 _mockParameter 中的 autoContext 中的標(biāo)示字符串放到 HTTP Header 中去。
@Component
public class MockHttpHeadSetting implements ClientRequestFilter {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
MultivaluedMap<String, Object> header = requestContext.getHeaders();
MockParameter mockParameter = MarketingBaseService.mockParameterThreadLocal.get();
if (mockParameter != null && StringUtils.isNotBlank(mockParameter.getTestingMockParam())) {
header.add("Mock-parameter", mockParameter.getTestingMockParam());
}
}
}
接著在 SPI(javax.ws.rs.ext.Providers ) 文件中配置即可
com.hujiang.marketingcloud.ruleengine.service.MockHttpHeadSetting
在整個(gè)微服務(wù)架構(gòu)的實(shí)踐中,工程界一直缺少探討的就是在微服務(wù)架構(gòu)的測(cè)試這塊,離我們比較近的是自動(dòng)化測(cè)試,因?yàn)樽詣?dòng)化測(cè)試基本上是所有系統(tǒng)都需要的。
但是有一塊我們一直沒有重視的就是 全鏈路壓力測(cè)試 這塊,在生產(chǎn)上進(jìn)行全鏈路的真實(shí)的壓力測(cè)試需要解決很多問題,比較重要的就是 DB 這塊,壓測(cè)的時(shí)候產(chǎn)生的所有交易數(shù)據(jù)不能夠參與結(jié)算、財(cái)務(wù)流程,這就需要借助 影子表 來解決,所有的數(shù)據(jù)都不會(huì)寫入最終的真實(shí)的交易數(shù)據(jù)中去。當(dāng)然還有其他地方都需要解決,一旦打開全鏈路壓測(cè)開關(guān),應(yīng)該需要處理所有產(chǎn)生數(shù)據(jù)的地方,這是一個(gè)龐大的工程,但是也會(huì)非常有意思。
本篇文章只是我們?cè)谶@塊的一個(gè)初步嘗試,我們會(huì)繼續(xù)擴(kuò)展下去,在下次產(chǎn)線全鏈路壓測(cè)的時(shí)候我們就可以借助現(xiàn)在的實(shí)踐架構(gòu)擴(kuò)展起來。
作者:王清培 (滬江集團(tuán)資深JAVA架構(gòu)師)
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。