因目前公司内部对 http 接口自动化,dubbo 接口自动化都是使用脚本来管理,技术栈还分 java 和 python ,内部对接很不方便,为了降低使用门槛,提升接口测试效率,所以决定在原有的功能用例管理平台添加接口自动化来统一管理。
首先吹一波 MeterSphere,个人觉得目前最好,功能最完善的接口自动化平台,项目还在持续更新,并且有开源版,大家有兴趣可以看一下项目代码https://github.com/metersphere/
个人对于这个平台 Http 接口测试的部分理解:
通过前端的入参将其封装成 JMeter 能识别的 .jmx 文件,再通过 JMeter 的开放 API 去执行.jmx 文件。这里不得不说 该项目成员对 JMeter 非常熟悉,抛弃了 jmeter 难用的 GUI,实现了一套非常好用 UI,MeterSphere 牛皮。
因为只需要接口测试的功能,所以只移接口测试部分。首先要做的就是在本地部署 MeterSphere,官方文档只有 docker 部署教程,没有 windows 下搭建的教程,而且 sql 文件还分别放在不同的文件下面,这无疑增加了二次开发 的成本。
最后我在 linux 下部署项目,然后将 sql 导出,在 application.properties 添加数据库配置,注释了原项目中的部分代码,在 windows 下成功启动,并且移植了接口测试模块的功能,然后,在测试的时候发现 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 版本的原因
先来看两张图
我在 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 支持 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,还是很有成就感的。