針對Dubbo接口Mock的解決方案詳解
背景
為了提升減輕測試回歸壓力,提高項目開發(fā)交付質(zhì)量,我們開發(fā)和測試團隊合作在部分項目內(nèi)執(zhí)行自動化測試。
為了更好的理解,我們對mock的需要,先來看下wiki上對自動化測試的解釋。
在軟件測試中, 自動化測試指的是使用獨立于待測軟件的其他軟件來自動執(zhí)行測試、比較實際結果與預期并生成測試報告這一過程。
我們的自動化測試是按照我們預設的流程執(zhí)行的,我們不希望受到第三方服務的影響(上下線,接口返回錯誤數(shù)據(jù)),所以在調(diào)用三方接口的時候,我們會采取mock,返回我們預期的數(shù)據(jù)。
在自動化測試中,我們針對http,dubbo,mq消息這三種接口進行了mock,本文講解的是我們對dubbo接口進行mock的解決方案。
Dubbo目前提供方案
首先我們來看下dubbo框架本身提供的mock特性。
dubbo mock特性的核心代碼如下
//from MockClusterInvoker
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
//獲取方法級別mock配置
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
//沒有配置 或者 =false
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//no mock
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
// force 開頭 強制進行mock
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
//force:direct mock
result = doMockInvoke(invocation, null);
} else {
//不是force的話 是失敗了再進行mock
//fail-mock
try {
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
//如果是業(yè)務異常不進行mock
if (e.isBiz()) {
throw e;
}
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
result = doMockInvoke(invocation, e);
}
}
return result;
}針對url中key=mock對應value的不同,分別對應3種邏輯
- value = null 不走mock
- value = force xxx 強制走mock邏輯
- value = xxx 調(diào)用服務失敗后走mock邏輯
看第三個邏輯,有沒有感覺到這其實是一個降級,失敗降級,而第二個邏輯,就稱為強制降級了。
dubbo提供的官方文檔,也將這個mock定義為降級

想了解Dubbo Mock表達式具體如何配置,可以看 Dubbo之降級Mock源碼分析
是否滿足我們需求
我們的需求是
- 第三方是否在線不影響我們的mock
- 配置靈活簡單
經(jīng)過測試,在設置check=false之后,給接口配置mock=force:return null之后,如果提供者不在線,會拋出沒有提供者異常,不滿足需求1
測試方式,對dubbo官方demo 增加如下配置
//from DemoServiceComponent @Reference(mock = "force:return null",check = false) private DemoService demoService;
對于需求2,也存在以下問題
- 我們不可能去動原有項目中的dubbo配置,所以我們只能通過往dubbo的注冊中心增加override配置來觸發(fā)強制mock,使用上不方便
- 從第1點也可以看到,mock功能依賴注冊中心,我們的mock環(huán)境和測試環(huán)境都是使用同一個注冊中心,不可行
- mock value文檔不夠詳細,針對復雜類型的返回,構造費勁
所以結論是,實現(xiàn)上和使用上都不能滿足我們需求,Dubbo的mock功能還是專注于生產(chǎn)級別的降級需求,我們需要開發(fā)方便我們使用的mock方案。
我們開發(fā)的擴展方案
我們開發(fā)針對dubbo框架的mock方案設計要點如下
- 同樣的使用對Cluster擴展點包裝類來植入mock邏輯,保證服務下線不影響我們自動化測試運行
- 使用properties配置文件來管理接口的mock開關,配置可以托管到apollo,無代碼侵入
- 轉發(fā)請求到我司的EsayMock服務器,配置接口返回類型的Json數(shù)據(jù)即可

能不能用Filter來做
之前在網(wǎng)上看到過類似的方案是使用Filter來實現(xiàn)的,其實我們第一版也是通過Filter來做,但是存在一個問題,我們mock的接口的提供者必須在線。
下面從源碼的角度來解釋為何出現(xiàn)這個問題
在使用zookeeper為注冊中心,以及check=false的前提下
Filter邏輯的植入是通過Protocol的包裝類ProtocolFilterWrapper,ProtocolFilterWrapper會對除了RegistryProtocol的其他Protocol植入Filter調(diào)用鏈邏輯。
//from ProtocolFilterWrapper
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}問題就出在RegistryProtocol,RegistryProtocol通過Cluster,Directory模塊間接依賴了DubboProtocl,而在Directory模塊中,也就是RegistryDirectory中會對提供者數(shù)量進行檢查,如果為0,會拋出異常。這一切都發(fā)生在對DubboProtocl生成的invoker調(diào)用之前。
DubboProtocol生成的invoker,封裝了filter邏輯以及對遠端服務調(diào)用邏輯
RegistryProtcol生成的invoekr,在DubboProtocol基礎上封裝了集群調(diào)用,負載均衡等服務治理功能
//from RegistryDirectory
private void refreshInvoker(List<URL> invokerUrls) {
Assert.notNull(invokerUrls, "invokerUrls should not be null");
//這邊為什么是一個,針對沒有提供者目錄,dubbo框架會自動返回一個empty的url
if (invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // Forbid to access
this.invokers = Collections.emptyList();
routerChain.setInvokers(this.invokers);
destroyAllInvokers(); // Close all invokers
}
//...
}
public List<Invoker<T>> doList(Invocation invocation) {
if (forbidden) {
// 1. No service provider 2. Service providers are disabled
throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
", please check status of providers(disabled, not registered or in blacklist).");
}
//...
}沒看過dubbo源碼的朋友可能看不懂,你可以看了dubbo refer原理之后再來品味
不足
mock服務器中的json和dubbo接口不是強關聯(lián),不過問題不大,我們跑的都是預設流程。
開源項目
講了這么多,都是原理性的內(nèi)容,下面貼上鏈接,歡迎大家使用以及提建議。
以上就是針對Dubbo接口Mock的解決方案詳解的詳細內(nèi)容,更多關于Dubbo接口Mock解決的資料請關注腳本之家其它相關文章!
相關文章
Spring?MVC核心原理深度剖析:從請求到響應的魔法解密
本文將帶大家深入探索Spring?MVC的核心工作原理,不僅是為了應付面試,更是為了能在實際開發(fā)中寫出更高效、更健壯的Web應用,無論你是剛接觸Spring?MVC的新手,還是有一定經(jīng)驗的老兵,相信本文都能給你帶來新的啟發(fā),感興趣的朋友一起學習吧2025-08-08
關于訪問后端接口報404錯誤問題的解決方法(全網(wǎng)最細!)
404頁面的出現(xiàn)會降低用戶體驗,那么導致404頁面出現(xiàn)的原因是什么呢?這篇文章主要給大家介紹了關于訪問后端接口報404錯誤問題的解決方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-04-04
一文詳解Java17中LinkedList類的用法和應用場景
LinkedList 是 Java 集合框架中基于雙向鏈表實現(xiàn)的類,實現(xiàn)了 List 和 Deque 接口,本文將為大家介紹一下它在Java 17 中如何更高效的使用吧2025-03-03

