跟著小程學微服務-Mock自動化系統的原理及實現
一、前言
在之前的文章 http://www.jianshu.com/p/c128ed5c394e 中已經介紹了“自動化Mock系統0.9版本”,今天我將和大家一起探討我們的“自動化Mock系統1.0版本”。
二、測試人員面臨的測試問題
我公司目前用的是基於Dubbo的微服務改造,服務之間的呼叫鏈路冗長,每個服務又是單獨的團隊在維護,每個團隊又在不斷的演進和維護各個服務,那麼對測試人員將是非常大的挑戰。
測試人員每次進行功能測試的時候,測試用例每次都需要重新寫一遍,無法將測試用例的資料沉澱,尤其是做自動化測試的時候,測試人員準備測試資料就需要很長時間,效率非常低。
目前介面自動化測試框架也多種多樣,testng,junit,Fitnesse等,但都需要測試人員具備測試程式碼編寫能力,如果要做好和手工介面測試一樣效果的自動化測試更是需要大量的程式碼堆積,後期維護程式碼成本非常大。因此做成簡單配置用例流,無需編寫測試程式碼的系統是更貼合實際工作要求。
舉個例子:拿網際網路支付系統來說,某個團隊新增了支付交易的需求,這時候要進行測試,測試人員除了要測試支付交易需求本身是否正確,同時也要結合上下游的服務整體進行迴歸測試,這時候開發人員往往在支付交易系統中採用“硬編碼”的方式對上下游的系統進行“擋板”,如果測試人員對測試資料有所調整那麼“擋板”也要跟著調整,同時在專案正式上線的時候,如果開發人員沒有將“擋板”程式去除乾淨,將面臨嚴重的線上問題。
三、Dubbo的Mock功能
1、Dubbo的Mock使用
Dubbo自帶的Mock功能首先是為了做服務降級,比如某驗權服務,當服務提供方全部掛掉後,客戶端不丟擲異常,而是通過Mock資料返回授權失敗。
我們從官網上舉一個例子來說明:
<dubbo:reference interface="com.foo.BarService" mock="force" />
我們可以在期望的reference標籤上加一個mock=”force”,就可以將當前服務設定為mock。但是設定完mock屬性後還沒有結束,需要有一個Mock類對應我們的服務介面類。
規則如下:
介面名 + Mock字尾,服務介面呼叫失敗Mock實現類,該Mock類必須有一個無參建構函式。
對應到com.foo.BarService的話,則建立BarServiceMock類。
public class BarServiceMock implements BarService {
public String sayHello(String name) {
// 你可以偽造容錯資料,此方法只在出現RpcException時被執行
return "容錯資料";
}
}
經過以上設定後,當呼叫BarService進行遠端呼叫的話,直接請求到BarServiceMock類上面進行模擬測試。
2、Dubbo Mock的原理解析
在dubbo的配置檔案中classpath:/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.Cluster
可以看到如下配置列表:
mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
failfast=com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
failsafe=com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
failback=com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
forking=com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
available=com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
switch=com.alibaba.dubbo.rpc.cluster.support.SwitchCluster
mergeable=com.alibaba.dubbo.rpc.cluster.support.MergeableCluster
broadcast=com.alibaba.dubbo.rpc.cluster.support.BroadcastCluster
我們可以看到配置檔案中實際上有五大路由策略:
-
AvailableCluster: 獲取可用的呼叫。遍歷所有Invokers判斷Invoker.isAvalible,只要一個有為true直接呼叫返回,不管成不成功。
-
BroadcastCluster: 廣播呼叫。遍歷所有Invokers, 逐個呼叫每個呼叫catch住異常不影響其他invoker呼叫。
-
FailbackCluster: 失敗自動恢復, 對於invoker呼叫失敗, 後臺記錄失敗請求,任務定時重發, 通常用於通知。
-
FailfastCluster: 快速失敗,只發起一次呼叫,失敗立即保錯,通常用於非冪等性操作。
-
FailoverCluster: 失敗轉移,當出現失敗,重試其它伺服器,通常用於讀操作,但重試會帶來更長延遲。
Dubbo中預設使用的是FailoverCluster策略,而在實際執行的過程中是FailoverCluster會被先被注入到MockClusterWrapper中,過程就是:
Cluster$Adaptive -> 定位到內部key為failover的物件 ->FailoverCluster->注入到MockClusterWrapper
MockClusterWrapper內部會建立一個MockClusterInvoker物件。實際建立是封裝了FailoverClusterInvoker的MockClusterInvoker,這樣就成功地在Invoker之中植入了Mock機制。
我們來看MockClusterInvoker的內部實現:
- 如果在沒有配置之中沒有設定mock,那麼直接把方法呼叫轉發給實際的Invoker(也就是FailoverClusterInvoker)。
String mockValue = directory.getUrl().getMethodParameter(
invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
if (mockValue.length() == 0 || mockValue.equalsIgnoreCase("false"))
{
//no mock
result = this.invoker.invoke(invocation);
}
- 如果配置了強制執行Mock,比如發生服務降級,那麼直接按照配置執行mock之後返回。
else if (mockValue.startsWith("force"))
{
if (logger.isWarnEnabled())
{
logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url: " + directory.getUrl());
}
//force:direct mock
result = doMockInvoke(invocation, null);
}
- 如果是其它的情況,比如只是配置的是mock=fail:return null,那麼就是在正常的呼叫出現異常的時候按照配置執行mock。
try
{
result = this.invoker.invoke(invocation);
}
catch (RpcException rpcException) {
if (rpcException.isBiz()) {
throw rpcException;
}
else
{
if (logger.isWarnEnabled()) {
logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : "
+ directory.getUrl(), rpcException);
}
result = doMockInvoke(invocation, rpcException);
}
}
3、Dubbo Mock的適用場景
Dubbo的Mock功能主要是為了做服務降級而使用的,服務提供方在客戶端執行容錯邏輯,在出現RpcException(比如網路失敗,超時等)時進行容錯,然後執行降級Mock邏輯。自身並不適合做Mock測試系統。
四、自動化Mock系統的實現
1、Mock系統的簡單用例圖
2、Mock系統的架構圖
為了基於Dubbo實現Mock功能,需要對Dubbo原始碼進行一些必要的修改,通過上面的架構圖我們可以看到,實際上我們正是利用了Dubbo的Filter chain過濾器鏈這一機制實現的,為了方便大家更好的理解,下面將簡單介紹一下Dubbo的Filter機制。
2.1、Dubbo的Filter原理分析
Filter:是一種遞迴的鏈式呼叫,用來在遠端呼叫真正執行的前後加入一些邏輯,跟aop的攔截器servlet中filter概念一樣的。
Filter介面定義:
@SPI
public interface Filter {
Result invoke(Invoker<?> invoker,Invocation invocation) throws RpcException;
}
Filter的實現類需要打上@Activate註解, @Activate的group屬性是個string陣列,我們可以通過這個屬性來指定這個filter是在consumer, provider還是兩者情況下啟用,所謂啟用就是能夠被獲取,組成filter鏈。
List<Filter> filters =ExtensionLoader.getExtensionLoader(Filter.class).getAct ivateExtension(invoker.getUrl(),key, group);
Key就是SERVICE_FILTER_KEY還是REFERENCE_FILTER_KEY
Group就是consumer或者provider
關於SPI的詳細介紹請大家參考我之前寫的另一篇文章http://www.jianshu.com/p/46aa69643c97
ProtocolFilterWrapper:在服務的暴露與引用的過程中根據KEY是PROVIDER還是CONSUMER來構建服務提供者與消費者的呼叫過濾器鏈,Filter最終都要被封裝到Wrapper中的。
public <T> Exporter<T> export(Invoker<T>invoker)throws RpcException {
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
public <T> Invoker<T> refer(Class<T> type,URL url)throws RpcException {
return buildInvokerChain(protocol.refer(type, url),Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
構建filter鏈,當我們獲取啟用的filter集合後就通過ProtocolFilterWrapper類中的buildInvokerChain方法來構建。
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
public Result invoke(Invocation invocation)throws RpcException {
return filter.invoke(next, invocation);
}
。。。。。。。 //其他方法
};
}
2.2、Mock流程介紹
注:我們在<dubbo:application name>中新加了自定義的“env=test”這樣的屬性配置用來標明當前環境是測試的還是正式的,使用者每次通過Dubbo請求的遠端服務的時候,都會首先經過我們自定義的Filter,我們自定義的Filter會首先判斷當前的環境是test還是正式,如果是test的環境則直接訪問Mock配置中心獲取提前配置好的Mock資料並封裝成使用者定義的Response物件返回。
3、Mock系統的配置中心
Mock配置中心就是使用者將mock資料與應用環境建立關係的系統,整個系統就像一個工作流引擎:
環境設定->應用名稱設定->擋板規則設定->Facade服務介面設定->方法規則設定
- 環境設定
注:如果尚未對映來源IP地址到環境,則點選環境列表導航連結,進入環境列表頁面,點選新增,輸入源IP及環境名,點選確定按鈕,實現源IP到所設環境的對映。每個使用者都可以建立屬於自己的測試環境。
- 應用名稱設定
注:建立所使用系統的應用名稱,Mock配置中心預設使用<dubbo:application name>中的名稱作為應用名稱。
- 擋板規則
注:每一個擋板規則都是由一個環境名稱和應用名稱組成的唯一擋板,在擋板設定中選擇環境名稱和應用名稱,並且設定擋板的有效狀態。
- Facade規則
注:每一個Facade就是一個Dubbo的服務介面類,在這裡將自己的Facade名稱與全路徑與擋板名稱對應,以標識哪些Facade服務介面類是屬於哪個擋板的。
- 方法規則
注:方法規則是用來設定每個Facade中的需要mock的方法的,可以對不同的方法設定方法執行時間、方法丟擲的異常等等。
4、Mock系統的其他功能
由於不少應用專案開發完後想對其進行單獨壓測,而很多時候應用系統和其他業務系統形成了依賴關係,如果不佈署其他應用系統則無法完成壓測,為了更好的支援效能測試組進行擋板壓測,Mock系統支援壓測功能,而Mock系統自身也可以達到單臺伺服器1000TPS以上(8C8G)。
相關文章
- PXE實現系統自動化安裝
- 跟著小白學zookeeper: 分散式鎖的實現分散式
- 對微服務實現工作流自動化的一些注意點微服務
- 財務管理系統如何幫助企業實現財務自動化管理?
- 跟著《架構探險》學輕量級微服務架構 (一)架構微服務
- 跟著《架構探險》學輕量級微服務架構 (二)架構微服務
- 試著使用 jmeter 實現介面自動化測試JMeter
- Cobbler實現自動化安裝作業系統作業系統
- 業務系統成功微服務化改造的實施步驟微服務
- 三.Go微服務--令牌桶實現原理Go微服務
- CRM系統實現自動化的“三部曲”
- 應對CC攻擊的自動防禦系統——原理與實現
- python自動化審計及實現Python
- 透過CRM系統實現工作流程自動化
- 基於OpenTelemetry實現Java微服務呼叫鏈跟蹤Java微服務
- 快速實現現存系統微服務改造 博雲微服務治理產品新升級微服務
- 微服務通訊之ribbon實現原理微服務
- 跟著原始碼學IM(九):基於Netty實現一套分散式IM系統原始碼Netty分散式
- BloomFilter 原理,實現及優化OOMFilter優化
- Android UI 自動化測試實現過程AndroidUI
- 實現 UML 模型的自動化比較及合併模型
- 跟著 AI 一個小時學會 PythonAIPython
- 如何在一分鐘內實現微服務系統下的架構視覺化微服務架構視覺化
- 在微服務領域Spring Boot自動伸縮如何實現微服務Spring Boot
- 自動化和數字化在 ERP 系統中意味著什麼?
- synchronized實現原理及鎖優化synchronized優化
- 直播系統原始碼,自動登入及記住密碼實現原始碼密碼
- 動效實戰!跟著TUBIK STUDIO學習UI動效的常見用法UI
- 雲控系統的實現原理
- 小墨學習記--微服務微服務
- 微服務專案Git倉庫自動化指令碼微服務Git指令碼
- 微服務實戰(七):落地微服務架構到直銷系統(實現命令與命令處理器)微服務架構
- Jenkins+GitLab+Docker+SpringCloud+Kubernetes實現可持續自動化微服務JenkinsGitlabDockerSpringGCCloud微服務
- Selenium自動化實現web自動化-1Web
- 微服務~Eureka實現的服務註冊與發現及服務之間的呼叫微服務
- ARouter原理剖析及手動實現
- 微服務架構的核心要點和實現原理解析微服務架構
- Liunx環境下oracle 自動跟隨系統啟動Oracle