作者:王伟 编辑:毕小烦

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

Dubbo 简介

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

如下图所示:
如果你想玩转 Dubbo 接口测试?一定要知道这 3 种姿势 - 图1

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

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

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

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

Dubbo 是什么

官网上是这样介绍的:

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

同样,来自 Dubbo 官方的架构图如下所示:
如果你想玩转 Dubbo 接口测试?一定要知道这 3 种姿势 - 图2
先提供服务,再消费服务,Dubbo 接口测试的逻辑都在这张图上了。

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

Dubbo 要测什么

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

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

图来自网络

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

那 RPC 是什么?

维基百科:

在分布式计算中,RPC(Remote Procedure Call,远程过程调用)是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。 RPC 是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求 - 接受回应进行信息交互的系统。

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

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

Dubbo 要怎么测

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

如下图所示:
如果你想玩转 Dubbo 接口测试?一定要知道这 3 种姿势 - 图4

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

做好准备:提供服务

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

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

  • Dubbo 版本:2.7.12
  • 服务的应用名:demo-provider
  • 服务的端口(默认):20880
  • 服务的注册地址:zookeeper://127.0.0.1:2181
  • 被测接口的全限定名:org.apache.dubbo.samples.provider.DemoService
  • 接口的分组:略
  • 接口的版本:略

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

  1. public interface DemoService {
  2. // 一个简单的参数
  3. String singleParamMethod(String name);
  4. // 多个参数
  5. String multiParamMethod(String name, List<String> topics);
  6. // 参数是一个 JavaBean 对象
  7. String beanParamMethod(Info info);
  8. }

DemoService 中用到的 JavaBean 对象:

  1. public class Info implements Serializable {
  2. private String name;
  3. private int age;
  4. private String address;
  5. // get、set 略
  6. public Info() {
  7. }
  8. }

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

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

第 1 种姿势:敲命令

Telnet

Telnet 是什么?

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

Telnet 常用命令:

  1. # Telnet 到主机的默认端口
  2. $ telnet host
  3. # Telnet 到主机的指定端口
  4. $ telnet ip_address port
  5. # 退出 Telnet 会话
  6. $ quit

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

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

以 Dubbo 2.7.12 为例:

  1. # 连接到 dubbo
  2. $ telnet localhost 20880
  3. Trying 127.0.0.1...
  4. Connected to localhost.
  5. Escape character is '^]'.
  6. # 查看都支持哪些命令
  7. dubbo>help
  8. Please input "help [command]" show detail.
  9. cd [service] - Change default service.
  10. clear [lines] - Clear screen.
  11. count [service] [method] [times] - Count the service.
  12. pwd - Print working default service.
  13. exit - Exit the telnet.
  14. help [command] - Show help.
  15. invoke [service.]method(args) - Invoke the service method.
  16. ls [-l] [service] - List services and methods.
  17. log level - Change log level or show log
  18. ps [-l] [port] - Print server ports and connections.
  19. select [index] - Select the index of the method you want to invoke.
  20. shutdown [-t <milliseconds>] - Shutdown Dubbo Application.
  21. status [-l] - Show status.
  22. trace [service] [method] [times] - Trace the service.
  23. # 查看服务的接口列表
  24. dubbo>ls
  25. PROVIDER:
  26. org.apache.dubbo.samples.api.DemoService
  27. # 查看指定接口的方法列表
  28. dubbo>ls org.apache.dubbo.samples.api.DemoService
  29. org.apache.dubbo.samples.api.DemoService (as provider):
  30. multiParamMethod
  31. beanParamMethod
  32. singleParamMethod
  33. # 切到指定(默认)接口
  34. dubbo>cd org.apache.dubbo.samples.api.DemoService
  35. Used the org.apache.dubbo.samples.api.DemoService as default.
  36. You can cancel default service by command: cd /
  37. # 可直接调用这个接口的方法
  38. dubbo>invoke singleParamMethod("testingweekly")
  39. Use default service org.apache.dubbo.samples.api.DemoService.
  40. result: "Name: testingweekly"
  41. elapsed: 2 ms.
  42. # 如果不切换到指定接口,也可以这样使用全路径调用接口的方法:
  43. # invoke org.apache.dubbo.samples.api.DemoService.singleParamMethod("testingweekly")
  44. # invoke org.apache.dubbo.samples.api.DemoService.multiParamMethod("testingweekly",["tools","articles"])
  45. # 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 的用法:

  • 第 2 种姿势:写代码

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

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

直接引用

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

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

  1. <!-- Spring单元测试依赖 -->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-test</artifactId>
  5. <version>4.2.5.RELEASE</version>
  6. </dependency>
  7. <!-- dubbo-->
  8. <dependency>
  9. <groupId>org.apache.dubbo</groupId>
  10. <artifactId>dubbo</artifactId>
  11. <version>2.7.12</version>
  12. </dependency>
  13. <!-- ZooKeeper -->
  14. <dependency>
  15. <groupId>org.apache.curator</groupId>
  16. <artifactId>curator-framework</artifactId>
  17. <version>4.2.0</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.apache.curator</groupId>
  21. <artifactId>curator-recipes</artifactId>
  22. <version>4.2.0</version>
  23. </dependency>
  24. <!-- testNG -->
  25. <dependency>
  26. <groupId>org.testng</groupId>
  27. <artifactId>testng</artifactId>
  28. <version>6.14.3</version>
  29. <scope>test</scope>
  30. </dependency>
  31. <!-- 你要测试的服务提供者(Provider):按实际信息修改 -->
  32. <dependency>
  33. <groupId>***</groupId>
  34. <artifactId>demo-povider</artifactId>
  35. <version>*.*.*-SNAPSHOT</version>
  36. </dependency>

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

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

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

配置的内容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
  4. xmlns="http://www.springframework.org/schema/beans"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
  6. http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
  7. <!-- 待测服务的应用名 -->
  8. <dubbo:application name="demo-provider"/>
  9. <!-- 注册中心 -->
  10. <dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="60000" check="false"/>
  11. <!-- 要测试的接口服务 -->
  12. <dubbo:reference id="demoService"
  13. interface="org.apache.dubbo.samples.api.DemoService"
  14. timeout="10000" check="false"/>
  15. </beans>

Tips: 更多配置可参考:https://dubbo.apache.org/zh/docs/references/configuration/xml/

STEP 3. 编写测试用例

  1. @ContextConfiguration(locations = "classpath:dubbo-consumer.xml")
  2. public class DirectRefTest extends AbstractTestNGSpringContextTests {
  3. @Autowired
  4. private DemoService demoService;
  5. @Test
  6. public void singleParamMethodTest() {
  7. String result = demoService.singleParamMethod("软件测试周刊");
  8. System.out.println(result);
  9. }
  10. @Test
  11. public void multiParamMethodTest() {
  12. List<String> list = new ArrayList<String>();
  13. list.add("测试周刊");
  14. list.add("测试工具");
  15. String result = demoService.multiParamMethod("软件测试周刊", list);
  16. System.out.println(result);
  17. }
  18. @Test
  19. public void multiParamMethodTest() {
  20. List<String> list = new ArrayList<String>();
  21. list.add("测试周刊");
  22. list.add("测试工具");
  23. String result = demoService.multiParamMethod("软件测试周刊", list);
  24. System.out.println(result);
  25. }
  26. }

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

泛化调用

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

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

Dubbo 官网是这样说的:

泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。 更多请看:https://dubbo.apache.org/zh/docs/advanced/generic-reference/

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

测试用例如下:

  1. public class GenericTest {
  2. @Test
  3. public void testDemoService() {
  4. // 1.引用远程服务
  5. ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
  6. // 2. 设置服务提供者的应用名
  7. reference.setApplication(new ApplicationConfig("demo-provider"));
  8. // 3. 设置注册中心地址
  9. reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
  10. // 4. 设置要测试的接口信息
  11. // 接口名
  12. reference.setInterface("org.apache.dubbo.samples.api.DemoService");
  13. // 及其他信息
  14. // reference.setVersion("");
  15. // reference.setGroup("");
  16. // 5. 将其声明为泛化接口
  17. reference.setGeneric(true);
  18. // 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
  19. GenericService genericService = reference.get();
  20. List<String> topics = new ArrayList<String>();
  21. topics.add("测试周刊");
  22. topics.add("测试工具");
  23. topics.add("谁在招测试?");
  24. // 泛化调用的方法:
  25. // $invoke(String method, String[] parameterTypes, Object[] args)
  26. // $invoke(String 方法名, String[] 方法的参数类型, Object[] 传给这个接口的参数)
  27. // 例1:调用一个简单的单类型方法
  28. Object result1 = genericService.$invoke("singleParamMethod", new String[]{"java.lang.String"}, new Object[]{"软件测试周刊"});
  29. // 例2:调用一个多参数的方法
  30. // Object result2 = genericService.$invoke("testMethod2", new String[]{"java.lang.String", "java.util.List"}, new Object[]{"李四", Collections.singleton("杭州")});
  31. Object result2 = genericService.$invoke("multiParamMethod", new String[]{"java.lang.String", "java.util.List"}, new Object[]{"软件测试周刊",topics});
  32. // 例3:调用一个参数是 Java Bean 的方法
  33. Map<String, Object> info = new HashMap<>();
  34. info.put("name", "软件测试周刊");
  35. info.put("age", 2);
  36. info.put("address", "https://www.yuque.com/bxiaofan/testingweekly");
  37. Object result3 = genericService.$invoke("beanParamMethod", new String[]{"org.apache.dubbo.samples.api.DemoService$Info"}, new Object[]{info});
  38. System.out.println(result1);
  39. System.out.println(result2);
  40. System.out.println(result3);
  41. }

第 3 种姿势:用工具

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」

如下图所示:
image.png
STEP 3. 配置 Dubbo Sample

① 测试一个简单的单类型方法:
image.png
说明:

  • 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 格式数据。

关于参数类型:

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

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

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

执行后的结果如下:
image.png

Postman

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

如下所示:
如果你想玩转 Dubbo 接口测试?一定要知道这 3 种姿势 - 图10

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

使用时是这样的:
image.png

总结

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

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

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

参考资料

(完)

微信搜索“毕小烦”或者扫描下面的二维码,即可订阅我的微信公众号
image.png
如果文章对你有帮助,记得留言、点赞、加关注哦!