前言
Dubbo服务测试方法介绍(一)
通过上篇文章介绍了Dubbo接口测试通过命令的方式进行请求测试。这篇文章主要介绍将命令行请求的方式通过代码封装,集成到Jmeter中、接口平台中,形成工具可以在工作中随手即用。
代码
上篇文章中我们是根据dubbo提供者部署服务器地址进行访问的,当我们的服务通过docker或其他容器部署时,每次部署ip会进行改变,这时就需要zookeeper注册中心实时获取提供者ip,然后进行测试。所以此次代码中获取提供者ip通过zookeeper注册中心实时获取。
这篇文章主要讲解集成Jmeter,所以需要创建java project项目。不过代码逻辑是统一的,后续如果想要集成到平台中,可以将代码集成到springBoot项目中去,通过接口进行自动化方面的测试集成。
第一步:创建java project工程项目
第二步:编写代码完成后,打成jar包,将jar包copy到jmeter的{Jmeter_home}\lib\ext文件夹下
通过Jmeter调用自定义Jar时,有多种方式,其中一个是通过BeanShell取样器编写代码调用,另一种方式是通过Java请求的方式调用(推荐)如下图:
代码(BeanShell取样器调用)
package com.juzi.zk;
import org.I0Itec.zkclient.ZkClient;
import org.apache.commons.net.telnet.TelnetClient;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
/**
* @ClassName DubboTest
* @Description TODO
* @Author wangxiaowu
* @Date 2021/5/10 17:41
* @Version 1.0
*/
public class DubboTest {
private static ZkClient zkClient = null;
private static TelnetClient telnet;
private static InputStream in; // 输入流,接收返回信息
private static PrintStream out; // 向服务器写入 命令
private static String prompt = ">"; //结束标识字符串,Windows中是>,Linux中是#
private static char promptChar = '>'; //结束标识字符
public static void main(String[] args) {
String res = DubboTest.sendRequest("1.1.1.1:2181", "provider-test",
"com.aa.bb.ProductRemoteService",
"getProductProperty",
"{\"application\":\"aa\"}");
System.out.println(res);
}
public static String sendRequest(String conn,String applicationName,String service,String methods,String params){
// 判断zkClient是否为null,为null创建对象否则使用
if (zkClient==null){
zkClient = new ZkClient(conn,100000 );
telnet = new TelnetClient("VT220");
setPrompt(">");
}
// 此方法是通过zookeeper注册中心获取服务提供者部署机器的ip,返回格式为192.1.5.121:20780
String dubboConn = getDubboConn(service, applicationName);
// 字符串分割ip与端口,方便之后invoke命令行请求时调用
String[] split = dubboConn.split(":");
//拼接命令
String command = "invoke " + service + "." + methods + "(" + params + ")";
// 调用命令行模式发送dubbo请求,并返回结果
String res = sendDubbo(split[0], Integer.parseInt(split[1]), command);
// 命令行请求dubbo后返回内容会带相关命令字符,通过截取只将返回结果返回,其他字符祛除
String[] str = res.split("\r\n");
return str[0];
}
public static void setPrompt(String prompt) {
if (prompt != null) {
prompt = prompt;
promptChar = prompt.charAt(prompt.length() - 1);
}
}
public static String getDubboConn(String service, String applicationName){
// 定义变量用来保存唯一符合服务的变量
String path = null;
// 根据传入的服务名获取zookeeper中心提供此服务所有环境的提供者列表
List<String> list = zkClient.getChildren("/dubbo/"+service+"/providers"); // 获取此节点下的服务提供者
// 循环所有的提供者,根据应用名称获取想要的唯一值
for(String str : list){
try {
if (str.contains(applicationName)&&!str.contains("acm-provider-test2")){
path = java.net.URLDecoder.decode(str, "UTF-8");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// 获取ip
URI uri = null;
String ipPort = null;
try {
uri = new URI(path);
ipPort = uri.getHost()+":"+uri.getPort();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return ipPort;
}
public static void login(String ip, int port) {
try {
telnet.connect(ip, port);
in = telnet.getInputStream();
out = new PrintStream(telnet.getOutputStream());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void write(String value) {
try {
out.println(value);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String readUntil(String pattern) {
StringBuffer sb = new StringBuffer();
try {
char lastChar = (char) -1;
boolean flag = pattern != null && pattern.length() > 0;
if (flag)
lastChar = pattern.charAt(pattern.length() - 1);
char ch;
int code = -1;
while ((code = in.read()) != -1) {
ch = (char) code;
sb.append(ch);
//匹配到结束标识时返回结果
if (flag) {
if (ch == lastChar && sb.toString().endsWith(pattern)) {
return sb.toString();
}
} else {
//如果没指定结束标识,匹配到默认结束标识字符时返回结果
if (ch == promptChar)
return sb.toString();
}
//登录失败时返回结果
if (sb.toString().contains("Login Failed")) {
return sb.toString();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
public static String sendCommand(String command) {
try {
write(command);
return readUntil(prompt);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String sendDubbo(String ip, int port, String cmd) {
/**
* ls
* ls RepayPlanRemoteService
* invoke RepayPlanRemoteService.trial("+params+")
*/
login(ip, port);
String rs = sendCommand(cmd);
try {
rs = new String(rs.getBytes("ISO-8859-1"), "GBK"); //转一下编码
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return rs;
}
}
代码(Java请求调用)
package com.juzi.zk;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
/**
* @ClassName DubboJmeter
* @Description TODO
* @Author wangxiaowu
* @Date 2021/5/11 9:51
* @Version 1.0
*/
public class DubboJmeter extends AbstractJavaSamplerClient {
/**
* 定义变量,赋值Jmeter中java请求时传入的参数
*/
String dubboConn = "";
String applicationName = "";
String serviceClassPatn = "";
String serviceMethods = "";
String serviceParams = "";
/**
* 用来自定义参数的方法,Jmeter中的java请求取样器中的入参定义方法
* 每个线程测试前执行一次,做一些初始化工作
* @return
*/
@Override
public Arguments getDefaultParameters() {
// 创建自定义参数的对象
Arguments params = new Arguments();
// zookeeper注册中心地址,ip及端口的拼接
params.addArgument("dubboConn", "1.1.1.1:2181");
// 提供者的应用名称
params.addArgument("applicationName", "acm-e-test");
// dubbo请求的service的路径
params.addArgument("serviceClassPatn", "com.a.b.c.r");
// dubbo请求的service的方法
params.addArgument("serviceMethods", "a");
// dubbo请求的service的请求参数
params.addArgument("serviceParams", "{\"application\":\"q\"}");
return params;
}
/**
* 获取参数的方法,用来获取Jmeter中传入的参数
* @param context
*/
@Override
public void setupTest(JavaSamplerContext context) {
dubboConn = context.getParameter("dubboConn");
applicationName = context.getParameter("applicationName");
serviceClassPatn = context.getParameter("serviceClassPatn");
serviceMethods = context.getParameter("serviceMethods");
serviceParams = context.getParameter("serviceParams");
}
/**
* Jmeter中的请求主体,jmeter请求时运行的主要逻辑则为此方法
* @param javaSamplerContext
* @return
*/
@Override
public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
//new一个SampleResult对象,用来实现计时、结果回写等操作。
SampleResult sr=new SampleResult();
// try...catch,捕获一下异常
try {
//请求开始计时
sr.sampleStart();
// 调用通过zookeeper注册中心请求dubbo的方法并返回结果
String res = DubboTest.sendRequest(dubboConn, applicationName, serviceClassPatn, serviceMethods, serviceParams);
// 设置显示请求数据
sr.setSamplerData("本次请求参数 :\n注册中心:"+dubboConn+"\n请求路径:"+serviceClassPatn+"\n请求方法:"+serviceMethods+"\n请求参数:"+serviceParams);
// 设置显示数据
sr.setResponseData(res,null);
//设置请求的结束状态。
sr.setSuccessful(true);
}catch (Exception e){
sr.setResponseData("fail msg:"+e.getMessage(),sr.TEXT);
sr.setSuccessful(false);
}finally {
//请求结束计时。
sr.sampleEnd();
}
return sr;
}
}
Jmeter中Beanshell取样器的演示
将工程项目打成jar导入到jmeter指定目录后,打开Jmeter——>添加线程组——>添加Beanshell取样器,如图:
Jmeter中Java请求的演示
如果第三方jar包想使用Jmeter中的Java请求的方式调用,需要将Jmeter目录中{jmeter_home}\lib\ext的ApacheJMeter_core.jar、ApacheJMeter_java.jar两个jar包导入到项目工程中。
将工程项目打成jar导入到jmeter指定目录后,打开Jmeter——>添加线程组——>添加Java请求,如图:
至此Dubbo接口测试集成到Jmeter结束…