第一次感受到改原始碼解決 BUG 的樂趣

大佬喝可乐發表於2020-12-21

因目前公司內部對http介面自動化,dubbo介面自動化都是使用指令碼來管理,技術棧還分java 和python ,內部對接很不方便,為了降低使用門檻,提升介面測試效率,所以決定在原有的功能用例管理平臺新增介面自動化來統一管理。

MeterSphere

首先吹一波MeterSphere,個人覺得目前最好,功能最完善的介面自動化平臺,專案還在持續更新,並且有開源版,大家有興趣可以看一下專案程式碼https://github.com/metersphere/

個人對於這個平臺Http介面測試的部分理解:
通過前端的入參將其封裝成JMeter能識別的 .jmx檔案,再通過JMeter的開放API去執行.jmx檔案。

這裡不得不說 該專案成員對JMeter非常熟悉,拋棄了jmeter難用的GUI,實現了一套非常好用UI,MeterSphere牛皮。

移植MeterSphere部分功能

因為只需要介面測試的功能,所以只移介面測試部分。首先要做的就是在本地部署MeterSphere,官方文件只有docker部署教程,沒有windows下搭建的教程,而且sql檔案還分別放在不同的檔案下面,這無疑增加了二次開發 的成本。
最後我在linux下部署專案,然後將sql匯出,在application.properties新增資料庫配置,註釋了原專案中的部分程式碼,在windows 下成功啟動,並且移植了介面測試模組的功能,然後,在測試的時候發現Dubbo介面無法成功呼叫,於是便有了第一次修改原始碼的操作......

發現問題:dubbo介面測試報錯

MeterSphere這裡對Dubbo的配置很多,原本想簡化測試,這一套配置填下去,感覺比原有的方式還要麻煩,於是這裡我便將配置預設寫死,因為公司目前使用zk做註冊中心,其他的consumer&Service、Config Service這個配置也無需使用人員去配置。

在這裡插入圖片描述

但是當我填入對應的引數,請求dubbo介面的時候,問題就出現了

Failed to check the status of the service xxx.xxx.xxx . No provider available for the service

一直報錯找不到對應的服務,而我用現有的指令碼請求是能夠成功的,確認了環境沒有問題之後,我想可能是Dubbo版本的原因

定位問題:dubbo版本

先來看兩張圖
在這裡插入圖片描述

在這裡插入圖片描述
我在maven倉庫裡面搜出來結果可以發現,Dubbo是在2.7.x版本被apache收錄,2.6.x的版本 groupId是com.alibaba。
於是在專案中找到對應依賴,果然版本對不上,我們系統目前使用的Dubbo是2.5.x,阿里的版本。而這兩個版本連線ZK的方式也不同,老版本是通過

<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
</dependency>

新版本則是使用

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>

確定問題就是版本問,於是便開始了改原始碼

解決問題:修改jmeter-plugins-for-apache-dubbo外掛程式碼

Jmeter支援Dubbo介面需要jmeter-plugins-for-apache-dubbo這個三方外掛支援,官方目前使用的是2.7.12不適合我們系統

<dependency>
<groupId>io.metersphere</groupId>
<artifactId>jmeter-plugins-dubbo</artifactId>
<version>2.7.12</version>
</dependency>

於是我將其修改為官方提供的1.3.x,發現呼叫Dubbo介面還是失敗,無法找到對應的服務。

最後我將1.3.x三方包下載到本地,研究其泛化呼叫的程式碼,發現其中很大一部分程式碼其實是對不同註冊中心如:zookeeper、nacos、redis的支援,而公司目前使用的zookeeper,我完全可以將這些不用的程式碼註釋掉,自己封裝一套泛化呼叫的邏輯,

    @SuppressWarnings({"unchecked", "rawtypes"})
private Object callDubbo(SampleResult res) {

try {
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("remoteInvoke");
applicationConfig.setVersion("");
RegistryConfig registryConfig = new RegistryConfig();

registryConfig.setFile("/tmp/dubbo.cachr");
String address = getAddress();
registryConfig.setAddress(address);
registryConfig.setProtocol("zookeeper");
reference.setApplication(applicationConfig);
reference.setRegistry(registryConfig);

// 弱型別介面名
String interfaceName = getInterface();
reference.setInterface(interfaceName);
reference.setVersion("1.0.0");
// 宣告為泛化介面
reference.setGeneric(true);
reference.setProtocol("dubbo");
//不重試,重試會造成資料重複執行
reference.setRetries(0);
reference.setTimeout(10000);

String methodName = getMethod();
if (StringUtils.isBlank(methodName)) {
res.setSuccessful(false);
return ErrorCode.MISS_METHOD.getMessage();
}

// 用org.apache.dubboinfo.rpc.service.GenericService可以替代所有介面引用
GenericService genericService = reference.get();
if (genericService == null) {
res.setSuccessful(false);
return MessageFormat.format(ErrorCode.GENERIC_SERVICE_IS_NULL.getMessage(), interfaceName);
}
String[] parameterTypes = null;
Object[] parameterValues = null;
List<MethodArgument> args = getMethodArgs();
List<String> paramterTypeList = new ArrayList<String>();;
List<Object> parameterValuesList = new ArrayList<Object>();;
for(MethodArgument arg : args) {
ClassUtils.parseParameter(paramterTypeList, parameterValuesList, arg);
}
parameterTypes = paramterTypeList.toArray(new String[paramterTypeList.size()]);
parameterValues = parameterValuesList.toArray(new Object[parameterValuesList.size()]);
Object result = null;
try {
result = genericService.$invoke(methodName, parameterTypes, parameterValues);
res.setSuccessful(true);
} catch (Exception e) {
log.error("RpcException:", e);
//TODO
//當介面返回異常時,sample標識為successful,通過響應內容做斷言來判斷是否標識sample錯誤,因為sample的錯誤會統計到用例的error百分比內。
//比如介面有一些校驗性質的異常,不代表這個操作是錯誤的,這樣就可以靈活的判斷,不至於正常的校驗返回導致測試用例error百分比的不真實
res.setSuccessful(true);
result = e;
}
return result;
} catch (Exception e) {
log.error("UnknownException:", e);
res.setSuccessful(false);
return e;
} finally {
//TODO 不能在sample結束時destroy
// if (registry != null) {
// registry.destroyAll();
// }
// reference.destroy();
}
}

最後,將程式碼打成jar包,通過maven離線呼叫

<dependency>
<groupId>io.metersphere</groupId>
<artifactId>jmeter-plugins-dubbo</artifactId>
<version>1.3.8</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/jmeter-plugins-dubbo-1.3.8.jar</systemPath>
</dependency>

終於請求成功
在這裡插入圖片描述

結語

正當我幻想著給MeterSphere提交issure,並提交pr,成為一個熱門開源專案的貢獻者,從此走上人生巔峰時。我發現MeterSphere專案上的提交記錄赫然寫著 :

"fix: 修復dubbo客戶端v2.7.7以上版本在進行泛化呼叫server端為v2.6.x以前版本時出現No Provider錯誤"
在這裡插入圖片描述

BUG其實在兩個月前被解決了...
雖然沒能成為MeterSphere 的貢獻者,但是第一次通過修改原始碼來解決BUG,還是很有成就感的。

相關文章