十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
標(biāo)簽:microServices autoTest mock unitTest testTrace
10年積累的網(wǎng)站設(shè)計(jì)、網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有振興免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
從 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)趨勢下,微服務(wù)在運(yùn)維層面和自動化部署方面基本上是比較完善了。從我個(gè)人經(jīng)驗(yàn)來看,上層的開發(fā)、測試對微服務(wù)架構(gòu)帶來的巨大變化還在反應(yīng)和學(xué)習(xí)中。
開發(fā)層面討論微服務(wù)的更多是框架、治理、性能等,但是從完整的軟件工程來看我們嚴(yán)重缺失分析、設(shè)計(jì)知識,這也是我們現(xiàn)在的工程師普遍缺乏的技術(shù)。
我們經(jīng)常會發(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,我們總是忽視單元測試背后產(chǎn)生的軟件工程的價(jià)值。)
我們有沒有發(fā)現(xiàn)一個(gè)現(xiàn)象,在整個(gè)軟件過程里,測試這個(gè)環(huán)節(jié)容易被忽視。任何一種軟件工程模型都有 QA環(huán)節(jié),但是這個(gè)環(huán)節(jié)似乎很薄很弱,目前我們絕大多數(shù)工程師、架構(gòu)師都嚴(yán)重低估了這個(gè)環(huán)節(jié)的力量和價(jià)值,還停留在無技術(shù)含量,手動功能測試低級效率印象里。
這主要是測試這個(gè)角色整個(gè)技術(shù)體系、工程化能力偏弱,一部分是客觀大環(huán)境問題,還有一部分自身問題,沒有讓自己走出去,多去學(xué)習(xí)整個(gè)工程化的技術(shù),多去了解開發(fā)的技術(shù),生產(chǎn)上的物理架構(gòu),這會有助于測試放大自己的聲音。
導(dǎo)致測試環(huán)節(jié)在國內(nèi)整個(gè)設(shè)計(jì)創(chuàng)新薄弱的原因還有一個(gè)主要原因就是,開發(fā)工程師普遍沒有完整的工程基礎(chǔ)。在國外IT發(fā)達(dá)國家,日本、美國等,一個(gè)合格的開發(fā)工程師、測試工程師都是邊界模糊的,自己開發(fā)產(chǎn)品自己測試,這需要切換思維模式,需要同時(shí)具備這兩種能力,但是這才是整個(gè)軟件工程的完整流程。
我們有沒有想過一個(gè)問題,為什么現(xiàn)在大家都在談?wù)?DevOps,而不是 DevTestOps,為什么偏偏跳過測試這個(gè)環(huán)節(jié),難道開發(fā)的系統(tǒng)需要具備良好的可運(yùn)維性就不需要可測試性嗎,開發(fā)需要具備運(yùn)維能力,運(yùn)維需要具備開發(fā)能力,為什么測試環(huán)節(jié)忽略了。
我們對 QA環(huán)節(jié)的輕視,對測試角色的不重視其實(shí)帶來的副作用是非常大的。
微服務(wù)的拆分粒度要比 SOA細(xì)了很多,從容器化鏡像自動部署來衡量,是拆小了之后很方便,但是拆小了之后會給整個(gè)開發(fā)、測試環(huán)節(jié)增加很大的復(fù)雜度和效率問題。
在 SOA時(shí)期,契約驅(qū)動這個(gè)原則在微服務(wù)里也一樣適用,跨部門需求定義好契約你就可以先開發(fā)上線了。但是這個(gè)里面最大的問題就是當(dāng)前系統(tǒng)的部分連調(diào)問題和自動化回歸問題,如果是新系統(tǒng)上線還需要做性能壓測,這外部的依賴如何解決。
也許我們會說,不是應(yīng)該依賴方先ready,然后我們緊接著進(jìn)行測試、發(fā)布嗎。如果是業(yè)務(wù)、架構(gòu)合理的情況下,這種場景最大的問題就是我們的項(xiàng)目容易被依賴方牽制,這會帶來很多問題,比如,研發(fā)人員需要切換出來做其他事情,branch一直掛著,不知道哪天突然來找你說可以對接了,也許這已經(jīng)過去一個(gè)月或者更久,這種方式一旦養(yǎng)成習(xí)慣性研發(fā)流程就很容易產(chǎn)生線上 BUG。
還有一種情況也是合理的情況就是平臺提供方需要調(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è)性化的營銷活動玩法,我們需要在 marketing-cloud規(guī)則引擎中抽象出業(yè)務(wù)方營銷活動的返回信息,同時(shí)打通個(gè)性化營銷活動與公共交易、結(jié)算環(huán)節(jié),形成一個(gè)完整的業(yè)務(wù)流。
這是一個(gè) marketing-cloud邏輯架構(gòu)圖,跟我們主題相關(guān)的就是 營銷規(guī)則引擎,他就是我們這里所說的合理的業(yè)務(wù)場景。
在整個(gè)正向下單過程中,營銷規(guī)則引擎要肩負(fù)起既要提供 marketing-cloud內(nèi)的共用營銷活動,還需要橋接外部營銷中心的各類營銷玩法,外部的營銷中心會有多個(gè),目前我們主要有兩個(gè)。
由于這篇文章不是介紹營銷平臺怎么設(shè)計(jì),所以這里不打算擴(kuò)展話題。主要是起到拋磚引玉的目的,平臺型的業(yè)務(wù)會存在各種各樣的對外系統(tǒng)依賴的業(yè)務(wù)場景。文章接下來的部分將展開 marketing-cloud 規(guī)則引擎在打通測試鏈路上的實(shí)踐。
在開發(fā)階段,我們會經(jīng)常性的編寫單元測試來測試我們的邏輯,在編寫 unitTest的時(shí)候都需要 mock周邊的依賴,mock出來的對象分為兩種類型,一種是不具有 Assert邏輯的 stub 樁對象,還有一種就是需要支持 Assert的 mocker模擬對象。
但是我們也不需要明顯區(qū)分他們,兩者的區(qū)別不是太明顯,在編碼規(guī)范內(nèi)可能需要區(qū)分。
我們關(guān)心的是如何解決對象之間的依賴問題,各種 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)邏輯使之更加便于測試,也就是代碼是否具備很好的可測試性,是否已經(jīng)消除了絕大多數(shù) private方法,private方法是否有某些指責(zé)是我們沒有捕捉到業(yè)務(wù)概念。
在我們完成了所有的開發(fā),完善的單元測試保證了我們內(nèi)部的邏輯是沒有問題的(當(dāng)然這里不討論 unitTest的 case的設(shè)計(jì)是否完善情況)。
現(xiàn)在我們需要對接周邊系統(tǒng)開發(fā)進(jìn)行連調(diào)了,這個(gè)周邊系統(tǒng)還是屬于本平臺之類的其他支撐系統(tǒng)。比如我們的 marketing-cloud 規(guī)則引擎系統(tǒng)與 下單系統(tǒng)之間的關(guān)系。在開發(fā)的時(shí)候我們編寫 unitTest是順利的完成了開發(fā)解決的驗(yàn)證工作,但是現(xiàn)在面對連調(diào)問題。
系統(tǒng)需要正式的跑起來,但是我們?nèi)狈ν獠繝I銷中心的依賴,我們怎么辦。其實(shí)我們也需要在連調(diào)階段 mock外部依賴,只不過這個(gè) mock的技術(shù)和方法不是通過 unitTest框架來支持,而是需要我們自己來設(shè)計(jì)我們的整個(gè)服務(wù)的開發(fā)架構(gòu)。
首先要能識別本次 request是需要 mock的,那就需要某種 mock parameter參數(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是營銷平臺對 CCTalk、滬江網(wǎng)校滬江兩大子公司營銷中心發(fā)起調(diào)用的 Facade。
(為了盡量還原我們的工程實(shí)踐干貨同時(shí)需要消除一些敏感信息的情況下,整篇文章所有的代碼實(shí)例,我都刪除了一些不影響閱讀且和本文無關(guān)的代碼,同時(shí)做了一些偽編碼和省略,使代碼更精簡更便于閱讀。)
在正常邏輯下,我們會根據(jù)營銷路由 key來決定調(diào)用哪個(gè)公司的營銷中心接口,但是由于我們在開發(fā)這個(gè)項(xiàng)目的時(shí)候暫時(shí)業(yè)務(wù)方還沒有存在的地址讓我們對接,所以我們自己做了 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 discountDtos = ...
request.getMarketingProductTagsParameter().getMarketingTags().forEach(item -> {
MarketingProductDiscountDto discountDto = ...
discountDto.setProductId(item.getProductID());
...
discountDtos.add(discountDto);
});
...
return response;
}
}
我們定義了兩個(gè) mock類,都是一些測試數(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;
/**
* 是否自動化測試 mock
*/
public Boolean useAutoTestMock;
/**
* 測試mock參數(shù)
*/
public String testMockParam;
}
我們暫且忽略通用型之類的設(shè)計(jì),這里只是我們在趕項(xiàng)目的情況下做的一個(gè)迭代嘗試,等我們把這整個(gè)流程都跑通了再來考慮重構(gòu)提取框架。
有了輸入?yún)?shù),我們就可以根據(jù)參數(shù)判斷來動態(tài)注入 mock facade。
我們繼續(xù)向前推進(jìn),過了連調(diào)階段緊接著就進(jìn)入測試環(huán)節(jié),現(xiàn)在基本上大多數(shù)互聯(lián)網(wǎng)公司都是自動化的測試,很少在有手動的,尤其是后端系統(tǒng)。
那么在 autoTest階段面臨的一個(gè)問題就是,我們需要一個(gè)公共的 autoTest地址,這個(gè)測試地址是不變的,我們在自動化測試下 mock的 facade bean的地址就是這個(gè)地址,這個(gè)地址輸出的值需要能夠?qū)?yīng)到每次自動化腳本執(zhí)行的上下文中。
我們有很多微服務(wù)系統(tǒng)來組成一個(gè)平臺,每個(gè)服務(wù)都有依賴的第三方接口,原來在自動化測試這些服務(wù)的時(shí)候都需要去了解業(yè)務(wù)方系統(tǒng)的接口、DB、前臺入口等,因?yàn)樵诰帉懽詣踊_本的時(shí)候需要同步創(chuàng)建測試數(shù)據(jù),最后才能 Assert。
這個(gè)跨部門的溝通和協(xié)作效率嚴(yán)重低下,而且人員變動、系統(tǒng)變動都會直接影響上線周期,這里絕對值得創(chuàng)新來解決這個(gè)效率嚴(yán)重阻塞問題。
@Value("${marketing.cloud.business.access.url.mock}")
private String mockUrl;
/**
* 自動化測試 mocker bean
*/
@Bean("CCMarketingCentralFacadeTestMock")
public CCMarketingCentralFacade CCMarketingCentralFacadeTestMock() {
RestClientProxyFactoryBean restClientProxyFactoryBean ...
restClientProxyFactoryBean.setBaseUri(this.mockUrl);
...
}
/**
* 自動化測試 mocker bean
*/
@Bean("ClassMarketingCentralFacadeTestMock")
public ClassMarketingCentralFacade ClassMarketingCentralFacadeTestMock() {
RestClientProxyFactoryBean restClientProxyFactoryBean ...
restClientProxyFactoryBean.setBaseUri(this.mockUrl);
...
}
這里的 mockUrl就是我們抽象出來的統(tǒng)一的 autoTest地址,在前面的 mock parameter中有一個(gè) useAutoTestMock Boolean類型的參數(shù),如果當(dāng)前請求此參數(shù)為 true,我們將動態(tài)注入自動化測試 mock bean,后續(xù)的所有調(diào)用都會走到 mockUrl指定的地方。
到目前為止,我們遇到了自動化測試統(tǒng)一的 mock地址要收口所有微服務(wù)在這方面的需求?,F(xiàn)在最大的問題就是,所有的微服務(wù)對外依賴的 response都不相同,自動化腳本在執(zhí)行的時(shí)候預(yù)先創(chuàng)建好的 response要能適配到當(dāng)前測試的上下文中。
比如,營銷規(guī)則引擎,我們的自動化腳本在創(chuàng)建一個(gè)訂單的時(shí)候需要預(yù)先構(gòu)造好當(dāng)前商品(比如,productID:101010),在獲取外部營銷中心提供的活動信息和抵扣信息的 response,最后才能去 Assert訂單的金額和活動信息記錄是否正確,這就是一次 autoTest context。
有兩種方式來識別當(dāng)前 autoTest context,一種是在 case執(zhí)行的時(shí)候確定商品ID,最后通過商品ID來獲取 mock的 response。還有一種就是支持傳遞 autoTest mock參數(shù)給到 mockUrl指定的服務(wù),可以使用這個(gè)參數(shù)來識別當(dāng)前測試上下文。
一個(gè)測試 case可能會穿過很多微服務(wù),這些所有的依賴服務(wù)可能都需要預(yù)設(shè) mock response,這基本上是一勞永逸的。
所以,我們抽象出了 autoTest Mock Gateway(自動化測試mock網(wǎng)關(guān)服務(wù)),在整個(gè)自動化測試環(huán)節(jié)還有很多需要支持的工作,服務(wù)之間的鑒權(quán),鑒權(quán) key的 mock,加解密,加解密 key的 mock,自動化測試 case交替并行執(zhí)行等。
作為工程師的我們都希望用系統(tǒng)化、工程化的方式來解決整體問題,而不是個(gè)別點(diǎn)狀問題。有了這個(gè) mock gateway我們可以做很多事情,也可以普惠所有需要的其他部門。
在一次 autoTest context里構(gòu)造好 mock response,然后通過 mock parameter來動態(tài)識別具體的來源服務(wù)進(jìn)行路由、鑒權(quán)、加解密等操作。
MockGateway是一個(gè)支點(diǎn),我相信這個(gè)支點(diǎn)可以撬動很多測試空間和創(chuàng)新能力。
接下來我們將展示在 marketing-cloud 營銷規(guī)則引擎中的初步嘗試。
自動化腳本在每跑一個(gè) case的時(shí)候會創(chuàng)建當(dāng)前 case對應(yīng)的 autoTestContext,這里面都是一些 meta data,用來表示這個(gè) case中所有涉及到的微服務(wù)系統(tǒng)哪些是需要走 mock gateway的。
在 mockGateway中所有的配置都是有一個(gè) autoTestContext所對應(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的基類,這樣才能保證所有的請求能夠正常的傳遞。
整個(gè)系統(tǒng)的開發(fā)架構(gòu)分層依賴是:facade->biz->service,基本的所有核心邏輯都是在 service中,請求的 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對象。
@Component
public class MarketingServiceImpl extends MarketingBaseService implements MarketingService {
@MockFacade(Setting = MockFacade.SETTING_FACADE_MOCK_BEAN)
public MarketingResult onlyExtendMarketingActivity(Marketing..Parameter tagsParameter) {
...
}
我們重點(diǎn)看下 @MockFacadeannotation 聲明。
@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中去和請求處理完時(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
這些邏輯完全基于一個(gè)約定,就是 MarketingBaseService,不具有通用型,只是在逐步的重構(gòu)和提取中,最終會是一個(gè) plugin框架。
public abstract class MarketingBaseService extends BaseService {
protected ClassMarketingCentralFacade classMarketingCentralFacade;
protected CCMarketingCentralFacade ccMarketingCentralFacade;
public static ThreadLocal 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中,可以動態(tài)的通過 AOP的方式來注入相應(yīng)的 mockerBean。
現(xiàn)在我們還要處理的就是對 mockGateway的調(diào)用將 _mockParameter 中的 autoContext中的標(biāo)示字符串放到 HTTP Header中去。
@Component
public class MockHttpHeadSetting implements ClientRequestFilter {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
MultivaluedMap 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)的測試這塊,離我們比較近的是自動化測試,因?yàn)樽詣踊瘻y試基本上是所有系統(tǒng)都需要的。
但是有一塊我們一直沒有重視的就是 全鏈路壓力測試這塊,在生產(chǎn)上進(jìn)行全鏈路的真實(shí)的壓力測試需要解決很多問題,比較重要的就是 DB這塊,壓測的時(shí)候產(chǎn)生的所有交易數(shù)據(jù)不能夠參與結(jié)算、財(cái)務(wù)流程,這就需要借助 影子表來解決,所有的數(shù)據(jù)都不會寫入最終的真實(shí)的交易數(shù)據(jù)中去。當(dāng)然還有其他地方都需要解決,一旦打開全鏈路壓測開關(guān),應(yīng)該需要處理所有產(chǎn)生數(shù)據(jù)的地方,這是一個(gè)龐大的工程,但是也會非常有意思。
本篇文章只是我們在這塊的一個(gè)初步嘗試,我們會繼續(xù)擴(kuò)展下去,在下次產(chǎn)線全鏈路壓測的時(shí)候我們就可以借助現(xiàn)在的實(shí)踐架構(gòu)擴(kuò)展起來。
作者:王清培 (滬江集團(tuán)資深JAVA架構(gòu)師)