查看原文
其他

如果你想玩转 Dubbo 接口测试?一定要知道这 3 种姿势

王伟 毕小烦 2022-07-13

| 作者:王伟  编辑:毕小烦


微服务盛行的今天,保障服务质量至关重要,作为测试人员,如何测试 Dubbo 接口呢?本文系统梳理了几种常见的测试方法,希望对大家有所启发。

一. Dubbo 简介

随着互联网行业的蓬勃发展和业务规模的持续增长,网站应用的规模也在不断扩大,同时也推动着互联网技术架构的持续演进:单一应用架构 -> 垂直应用架构 -> 分布式服务架构 -> 流动计算架构


如下图所示:


技术架构从 All in one 的大而全逐步演化为服务独立的小而精,服务数量越来越多,服务间的调用和依赖关系也越来越复杂,用于提升服务质量的 SOA(Service-Oriented Architecture,面向服务的架构) 自然就出现了,SOA 将单一进程的应用做了拆分,形成独立对外提供服务的组件,每个组件通过网络协议对外提供服务。


服务化架构继续演进,形成了更加细粒度的微服务架构(Microservices Architecture Pattern)。在微服务架构中,一个应用会被拆分为一个个独立、可配置、可运行、可维护的子服务,极大的方便了服务的复用,不同服务的组合也能够快速响应新的业务逻辑。


同时,随着敏捷开发、持续支付、DevOps 的发展与实践,以及 Docker 和 K8S 等容器化技术和平台的成熟,微服务架构已然流行起来。


Dubbo 正是微服务开发框架中的佼佼者。

1.1 Dubbo 是什么


官网上是这样介绍的:


Dubbo 是一款微服务开发框架,它提供了 RPC 通信微服务治理 两大关键能力。


这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。


同样,来自 Dubbo 官方的架构图如下所示:

先提供服务,再消费服务,Dubbo 接口测试的逻辑都在这张图上了。


接下来进入正题,看看 Dubbo 要测什么?

1.2 Dubbo 要测什么


不同应用之间通过 RPC 协议提供服务(Service 接口),而这些服务就是我们要测的内容。


下图是经典的测试金字塔:

| 图来自网络


从 ROI 来讲,自下而上效率越来越慢,成本则越来越高。从重要性来讲,服务这一层是核心,我们不仅要保障单个系统的输入输出没有问题,还要保障各系统的集成和交互没有问题。


那 RPC 是什么?


维基百科:


在分布式计算中,RPC(Remote Procedure Call,远程过程调用)是一个计算机通信协议。


该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。


RPC 是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求 - 接受回应进行信息交互的系统。


通俗的讲:


RPC 是指远程过程调用,也就是说两台服务器 A,B,一个应用部署在 A 服务器上,想要调用 B 服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。


在具备了一些基础知识之后,接下来就可以看如何测试了。

1.3 Dubbo 要怎么测


测试 Dubbo 接口大致可分为三种姿势(方式):敲命令、写代码和用工具,每种姿势都有其适用的场景。


如下图所示:


下面我们先准备好被测服务。

二. 做好准备:提供服务

既然要测试(消费)服务,首先得提供服务。


假设要测试的服务关键信息如下:

  • Dubbo 版本:2.7.12

  • 服务的应用名:demo-provider

  • 服务的端口(默认):20880

  • 服务的注册地址:zookeeper://127.0.0.1:2181

  • 被测接口的全限定名:org.apache.dubbo.samples.provider.DemoService

  • 接口的分组:略

  • 接口的版本:略


要测试的接口名为 DemoService,这里有几种典型的方法:

public interface DemoService {

// 一个简单的参数
String singleParamMethod(String name);

// 多个参数
String multiParamMethod(String name, List<String> topics);

// 参数是一个 JavaBean 对象
String beanParamMethod(Info info);

}

DemoService 中用到的 JavaBean 对象:

public class Info implements Serializable {
private String name;
private int age;
private String address;

// get、set 略

public Info() {

}
}

| 本例是在 dubbo-samples-api 的基础上修改的。

在确保 ZooKeeper 启动成功,且被测服务也启动成功之后,就可以进入测试环节了。

三. 第 1 种姿势:敲命令

3.1 Telnet


Telnet 是什么?


Telnet 协议是 Internet 远程登录服务的标准协议和主要方式,它为用户提供了在本地计算机上远程管理主机的能力。


Telnet 常用命令:

# Telnet 到主机的默认端口
$ telnet host

# Telnet 到主机的指定端口
$ telnet ip_address port

# 退出 Telnet 会话
$ quit

Dubbo 从 2.0.5 版本开始支持通过 telnet 命令来进行服务治理,但不同版本稍有区别。


对于使用 Telnet 而言, Dubbo 2.7.13 是一个分界线,之前使用 telnet IP PORT连接到 Dubbo 协议端口(默认是 20880),之后连接到 QOS 端口(默认是 22222),命令的使用大致相同。


以 Dubbo 2.7.12 为例:

# 连接到 dubbo
$ telnet localhost 20880
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

# 查看都支持哪些命令
dubbo>help
Please input "help [command]" show detail.
cd [service] - Change default service.
clear [lines] - Clear screen.
count [service] [method] [times] - Count the service.
pwd - Print working default service.
exit - Exit the telnet.
help [command] - Show help.
invoke [service.]method(args) - Invoke the service method.
ls [-l] [service] - List services and methods.
log level - Change log level or show log
ps [-l] [port] - Print server ports and connections.
select [index] - Select the index of the method you want to invoke.
shutdown [-t <milliseconds>] - Shutdown Dubbo Application.
status [-l] - Show status.
trace [service] [method] [times] - Trace the service.

# 查看服务的接口列表
dubbo>ls
PROVIDER:
org.apache.dubbo.samples.api.DemoService

# 查看指定接口的方法列表
dubbo>ls org.apache.dubbo.samples.api.DemoService
org.apache.dubbo.samples.api.DemoService (as provider):
multiParamMethod
beanParamMethod
singleParamMethod

# 切到指定(默认)接口
dubbo>cd org.apache.dubbo.samples.api.DemoService
Used the org.apache.dubbo.samples.api.DemoService as default.
You can cancel default service by command: cd /

# 可直接调用这个接口的方法
dubbo>invoke singleParamMethod("testingweekly")
Use default service org.apache.dubbo.samples.api.DemoService.
result: "Name: testingweekly"
elapsed: 2 ms.


# 如果不切换到指定接口,也可以这样使用全路径调用接口的方法:
# invoke org.apache.dubbo.samples.api.DemoService.singleParamMethod("testingweekly")
# invoke org.apache.dubbo.samples.api.DemoService.multiParamMethod("testingweekly",["tools","articles"])
# invoke org.apache.dubbo.samples.api.DemoService.beanParamMethod({"name": "testingweekly","age": 2,"address": "https://www.yuque.com/bxiaofan/testingweekly"})

注意:

  • macOS 需要安装 telnet 命令:brew install telnet

  • macOS 连接到 dubbo 服务之后,如果粘贴的内容有中文,会断开连接,所以我在例子中都改成了英文。


更多 Telnet 的用法:

  • https://dubbo.apache.org/zh/docs/v3.0/references/telnet/

  • https://dubbo.apache.org/zh/docs/v3.0/references/qos/

四. 第 2 种姿势:写代码

写代码测试 Dubbo 接口得搭建一个测试项目,可以使用 https://spring.io/quickstart 快速搭建一个 Spring Boot 项目。


写代码也分两种方式:直接引用和泛化调用。

4.1 直接引用

直接引用 Dubbo 的方式需要依赖被测服务的 JAR 包,然后就可以跟写单元测试一样写 Dubbo 接口的测试用例了。


STEP 1. 在 pom.xml 中添加关键依赖:

<!-- Spring单元测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!-- dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.12</version>
</dependency>
<!-- ZooKeeper -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<!-- testNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
<!-- 你要测试的服务提供者(Provider):按实际信息修改 -->
<dependency>
<groupId>***</groupId>
<artifactId>demo-povider</artifactId>
<version>*.*.*-SNAPSHOT</version>
</dependency>

注意:

如果是本地搭建的服务,也可以将被测服务的 JAR 包直接导入测试项目。


STEP 2. 在 resources 下新建一个 XML 文件,用于配置服务提供者的信息。


假设文件名是:dubbo-consumer.xml


配置的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

<!-- 待测服务的应用名 -->
<dubbo:application name="demo-provider"/>

<!-- 注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="60000" check="false"/>

<!-- 要测试的接口服务 -->
<dubbo:reference id="demoService"
interface="org.apache.dubbo.samples.api.DemoService"
timeout="10000" check="false"/>
</beans>

更多配置可参考:

https://dubbo.apache.org/zh/docs/references/configuration/xml/


STEP 3. 编写测试用例

@ContextConfiguration(locations = "classpath:dubbo-consumer.xml")
public class DirectRefTest extends AbstractTestNGSpringContextTests {

@Autowired
private DemoService demoService;

@Test
public void singleParamMethodTest() {

String result = demoService.singleParamMethod("软件测试周刊");

System.out.println(result);
}

@Test
public void multiParamMethodTest() {
List<String> list = new ArrayList<String>();
list.add("测试周刊");
list.add("测试工具");
String result = demoService.multiParamMethod("软件测试周刊", list);

System.out.println(result);
}

@Test
public void multiParamMethodTest() {

List<String> list = new ArrayList<String>();
list.add("测试周刊");
list.add("测试工具");
String result = demoService.multiParamMethod("软件测试周刊", list);

System.out.println(result);
}

}

直接引用的方式来测试 Dubbo 接口,需要依赖被测服务的 JAR 包,还要配置 XML,这些都很麻烦。比较方便的是写测试用例的时候可直接查看源码来获取所需的信息。

4.2 泛化调用


Dubbo 提供了泛化调用,当我们想调用 Dubbo 服务时,无需依赖相关 JAR 包,只要知道一个接口全限定名以及入参返参,就能调用到该服务。这就大大降低了服务提供者和消费者的耦合性。


很多 Dubbo 接口测试框架和平台就是基于此开发的。


Dubbo 官网是这样说的:


泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。


更多请看:

https://dubbo.apache.org/zh/docs/advanced/generic-reference/


Dubbo 泛化调用具体应该怎么用呢?


测试用例如下:

public class GenericTest {

@Test
public void testDemoService() {
// 1.引用远程服务
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();

// 2. 设置服务提供者的应用名
reference.setApplication(new ApplicationConfig("demo-provider"));
// 3. 设置注册中心地址
reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
// 4. 设置要测试的接口信息

// 接口名
reference.setInterface("org.apache.dubbo.samples.api.DemoService");
// 及其他信息
// reference.setVersion("");
// reference.setGroup("");

// 5. 将其声明为泛化接口
reference.setGeneric(true);


// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();

List<String> topics = new ArrayList<String>();
topics.add("测试周刊");
topics.add("测试工具");
topics.add("谁在招测试?");

// 泛化调用的方法:
// $invoke(String method, String[] parameterTypes, Object[] args)
// $invoke(String 方法名, String[] 方法的参数类型, Object[] 传给这个接口的参数)

// 例1:调用一个简单的单类型方法
Object result1 = genericService.$invoke("singleParamMethod", new String[]{"java.lang.String"}, new Object[]{"软件测试周刊"});

// 例2:调用一个多参数的方法
// Object result2 = genericService.$invoke("testMethod2", new String[]{"java.lang.String", "java.util.List"}, new Object[]{"李四", Collections.singleton("杭州")});
Object result2 = genericService.$invoke("multiParamMethod", new String[]{"java.lang.String", "java.util.List"}, new Object[]{"软件测试周刊",topics});

// 例3:调用一个参数是 Java Bean 的方法
Map<String, Object> info = new HashMap<>();
info.put("name", "软件测试周刊");
info.put("age", 2);
info.put("address", "https://www.yuque.com/bxiaofan/testingweekly");

Object result3 = genericService.$invoke("beanParamMethod", new String[]{"org.apache.dubbo.samples.api.DemoService$Info"}, new Object[]{info});

System.out.println(result1);
System.out.println(result2);
System.out.println(result3);

}

五. 第 3 种姿势:用工具

5.1 JMeter


JMeter 本身并不支持 Dubbo 接口的测试,需要依赖一个第三方插件:jmeter-plugins-for-apache-dubbo


在演示如何测试之前,得先准备好环境。


STEP 1. 下载 JMeter


下载地址:

https://jmeter.apache.org/download_jmeter.cgi 


我用的是最新的 Apache JMeter 5.5 版本。


STEP 2. 下载第三方插件

下载地址:

https://github.com/thubbo/jmeter-plugins-for-apache-dubbo


名称为: jmeter-plugins-dubbo-2.7.8-jar-with-dependencies.jar


STEP 3. 安装第三方插件

  1. jmeter-plugins-dubbo-2.7.8-jar-with-dependencies.jar 放到 apache-jmeter-5.5/lib/ext/ 下面。

  2. 重启 JMeter。


至此准备工作完毕。


怎么用呢?


STEP 1. 在「测试计划」上添加「线程组」

STEP 2. 在「线程组」上添加「Dubbo Sample」


如下图所示:

STEP 3. 配置 Dubbo Sample


① 测试一个简单的单类型方法:

说明:

  • Registry Center:注册中心用的是 zookeeper,注册地址(Address)是:127.0.0.1:2181。超时时间(Timeout)为 6000 ms;

  • RPC Protocol:dubbo:// (默认);

  • Interface:点 「Get Provider List」可以获取被测服务的接口列表和方法列表,下面的 Interface 和 Method 通过选择就可以了,不必再手动输入;

  • Args:

    • paramType:参数类型,分为简单数据类型和自定义数据类型。

    • paramValue:参数的值,基本类型和包装类型直接使用具体的值,自定义类型与 List 或者 Map 等使用 JSON 格式数据。


关于参数类型:

  • 简单数据类型:填写基本类型/基本类型包装类/引用类型,如,【"int", "double", "java.lang.Integer", "java.lang.Double", "java.lang.String","java.util.List", "java.util.Map"】。

  • 自定义数据类型:填写类的完整路径,如,【org.apache.dubbo.samples.api.Info】。

  • 类型对照表:https://github.com/thubbo/jmeter-plugins-for-apache-dubbo/wiki/ParameterComparisonTable

  • 复杂参数案例:https://github.com/thubbo/jmeter-plugins-for-apache-dubbo/wiki/FAQ


② 测试一个多参数的方法:



③ 测试一个参数为 Java Bean 的方法:



STEP 4. 添加「监听器」-> 查看结果树


执行后的结果如下:


5.2 Postman


我们无法直接使用 Postman 测试 Dubbo 接口,但可以自己开发一个接口服务,接收 Postman 传过来的 Dubbo 接口信息,然后通过泛化调用的方式调用相应的 Dubbo 接口,再把结果返回给 Postman 进行验证。


如下所示:


这种方式具有普适性,缺点就是会牺牲一些测试效率。


使用时是这样的:

总结

本文介绍了测试 Dubbo 接口的三种姿势:敲命令、写代码和用工具,每种姿势又有多种不同的使用方式,其中泛化调用被广泛应用于各种 Dubbo 测试工具、框架和平台之中,需重点掌握。当然,也有一些测试框架使用命令行(Telnet)的方式,不必细究。


有句话叫存在即合理,每种姿势都有其适用的场景,并没有绝对的好坏之分,我们在实际的测试过程中,也是要按需选用。


那么,你平时都在使用什么样的方式测试 Dubbo 接口呢?请留言告诉我。




参考资料

  • 《深入理解apache dubbo与实战》

  • https://dubbo.apache.org/zh/



推荐阅读:


点击下方卡片关注毕小烦,我们一起

成为更好的自己

▲ 点击上方卡片关注毕小烦,一起成为更好的自己


如果文章对你有帮助,记得留言、点赞、加关注哦!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存