Dubbo 接口测试
ceshiren.com 霍格沃兹测试开发学社
为什么使用 dubbo

Dubbo 应用场景

Dubbo 架构


协议支持

dubbo 支持协议
- dubbo3 triple
- dubbo2 协议
- rest 协议
- http 协议
- hessian 协议
- redis 协议
- thrift 协议
- gRPC 协议
- memcached 协议
- rmi 协议
- webservice 协议
各协议性能


快速开始 Quick Start
registry 环境准备
- 统一服务 zookeeper redis
- 多播地址
#zookeeper
cp conf/zoo_sample.cfg conf/zoo.cfg
bin/zkServer.sh start
#redis
redis-server
管理控制台安装
git clone https://github.com/apache/dubbo-admin.git
cd dubbo-admin
git tag
git checkout 0.3.0
mvn clean package -DskipTests
#vim dubbo-admin-server/src/main/resources/application.properties
mvn --projects dubbo-admin-server spring-boot:run -Dserver.port=8081
#http://127.0.0.1:8080

dubbo demo 环境
git clone https://github.com/apache/dubbo.git
cd dubbo
git tag
git checkout dubbo-2.7.4.1
cd dubbo-demo
provider demo 环境
public class GreetingServiceImpl implements GreetingService {
@Override
public String hello() {
return "Greetings!";
}
}
public class Application {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
context.start();
System.in.read();
}
}
provider 配置
<beans>
<dubbo:application name="demo-provider" metadata-type="remote">
<dubbo:parameter key="mapping-type" value="metadata"/>
</dubbo:application>
<dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
<dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="-1"/>
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<bean id="greetingService" class="org.apache.dubbo.demo.provider.GreetingServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.demo.DemoService" timeout="3000" ref="demoService" registry="registry1"/>
<dubbo:service version="1.0.0" group="greeting" timeout="5000" interface="org.apache.dubbo.demo.GreetingService"
ref="greetingService"/>
</beans>
consumer demo
public class Application {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
context.start();
DemoService demoService = context.getBean("demoService", DemoService.class);
GreetingService greetingService = context.getBean("greetingService", GreetingService.class);
new Thread(() -> {
while (true) {
String greetings = greetingService.hello();
System.out.println(greetings + " from separated thread.");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
consumer 配置
<beans>
<dubbo:application name="demo-consumer" >
<dubbo:parameter key="mapping-type" value="metadata"/>
<dubbo:parameter key="enable-auto-migration" value="true"/>
</dubbo:application>
<dubbo:metadata-report address="zookeeper://127.0.1:2181"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" check="true"
interface="org.apache.dubbo.demo.DemoService"/>
<dubbo:reference version="1.0.0" group="greeting" id="greetingService" check="false"
interface="org.apache.dubbo.demo.GreetingService"/>
</beans>
dubbo 接口测试方法
常用测试方法
- telnet
- rpc 调用
- 泛化调用
telnet 测试方法
- ls
- ls: 显示服务列表
- ls -l: 显示服务详细信息列表
- ls XxxService: 显示服务的方法列表
- ls -l XxxService: 显示服务的方法详细信息列表
- cd status pwd
- invoke
- help
# dubbo开启的telnet服务
telnet localhost 20880
nc localhost 20880
# qos服务开启的telnet服务
telnet localhost 22222
nc localhost 22222
invoke 调用
- 调用服务的方法
- invoke XxxService.xxxMethod(1234, "abcd", {"prop" : "value"}):
- 调用全路径服务的方法
- invoke com.xxx.XxxService.XxxService.xxxMethod(1234, "abcd", {"prop" : "value"})
- 调用服务的方法(自动查找包含此方法的服务)
- invoke xxxMethod(1234, "abcd", {"prop" : "value"}):
- 当有参数重载,或者类型转换失败的时候,可以通过增加 class 属性指定需要转换类
- invoke xxxMethod({"name":"zhangsan","age":12,"class":"org.xxx.Person"})
- invoke com.xxx.xxxApiService({"3":0.123, "class":"java.util.HashMap"})
telnet 功能不建议使用
- telnet 仅作为临时性的调试工具,并不适合编写测试用例
- 最新版本的 dubbo 中的 telnet 功能有 bug,无法使用
泛化调用
泛化调用场景与价值
泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。

Api 方式泛化调用
// 引用远程服务 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱类型接口名
reference.setInterface("com.xxx.XxxService");
reference.setVersion("1.0.0");
// 声明为泛化接口
reference.setGeneric(true);
// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();
// 基本类型以及Date,List,Map等不需要转换,直接调用
Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"});
// 用Map表示POJO参数,如果返回值为POJO也将自动转成Map
Map<String, Object> person = new HashMap<String, Object>();
person.put("name", "xxx");
person.put("password", "yyy");
// 如果返回POJO将自动转成Map
Object result = genericService.$invoke("findPerson", new String[]
{"com.xxx.Person"}, new Object[]{person});
json 泛化调用 2.7.12 版本之后支持
// 引用远程服务
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
// 弱类型接口名
reference.setInterface("com.xxx.api.service.TestService");
reference.setGroup("dev");
reference.setVersion("1.0");
reference.setRetries(0);
// RpcContext中设置generic=gson
RpcContext.getContext().setAttachment("generic","gson");
// 声明为泛化接口
reference.setGeneric(true);
reference.setCheck(false);
GenericService genericService = ReferenceConfigCache.getCache().get(reference);
// 传递参数对象的json字符串进行一次调用
Object res = genericService.$invoke("setUser", new String[]{"com.xxx.api.service.User"}, new Object[]{"{'name':'Tom','age':24}"});
System.out.println("result[setUser]:"+res); // 响应结果:result[setUser]:{name=Tom, class=com.xxx.api.service.User, age=24}
pb 协议的泛化调用
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱类型接口名
reference.setInterface(GenericService.class.getName());
reference.setInterface("com.xxx.XxxService");
// 声明为Protobuf-json
reference.setGeneric(Constants.GENERIC_SERIALIZATION_PROTOBUF);
GenericService genericService = reference.get();
Map<String, Object> person = new HashMap<String, Object>();
person.put("fixed64", "0");
person.put("int64", "0");
// 参考google官方的protobuf 3 的语法,服务的每个方法中只传输一个POJO对象
// protobuf的泛化调用只允许传递一个类型为String的json对象来代表请求参数
String requestString = new Gson().toJson(person);
// 返回对象是GoolgeProtobuf响应对象的json字符串。
Object result = genericService.$invoke("sayHello", new String[] {
"com.xxx.XxxService.GooglePbBasic$CDubboGooglePBRequestType"},
new Object[] {requestString});
Dubbo2 测试
以 consumer 的角色去调用
public class DubboTest {
static DemoService demoService;
@BeforeAll
static void beforeAll() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
context.start();
demoService = context.getBean("demoService", DemoService.class);
}
@Test
void sayHello() {
String res;
res = demoService.sayHello("seveniruby");
assertThat(res, startsWith("Hello seveniruby"));
res = demoService.sayHello("ceshiren.com");
assertThat(res, startsWith("Hello ceshiren.com"));
}
}
Dubbo3 接口测试
dubbo3 的主推协议 triple

dubbo3 的使用
- 定义 pb 数据格式
- 生成 dubbo3 stub
- 引入 provider 并编写实现
- 部署启动 provider 并注册到 registry
- 通过 consumer 调用
consumer 方式调用 dubbo3
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
context.start();
DemoService demoService = context.getBean("demoService", DemoService.class);
HelloRequest request = HelloRequest.newBuilder().setName("Hello").build();
HelloReply reply = demoService.sayHello(request);
System.out.println("result: " + reply.getMessage());
System.in.read();
dubbo 接口测试 python版
跨语言调用 dubbo 的方法与原理
- 利用 dubbo 的多协议支持能力,增加 http hessian webservice 协议的配置
- 需要部署阶段的变更
- 使用了性能较低的协议速度较慢
- 对功能用例的性能影响可以忽略
- 利用 dubbo 底层的 grpc 支持能力
- 不需要部署阶段的变更
- 支持多语言
- 直接使用 pb 协议发送,性能较快

添加 hessian 依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-hessian</artifactId>
<version>${revision}</version>
</dependency>
开启 hessian 支持
<dubbo:protocol name="dubbo" port="-1"/>
<dubbo:protocol name="hessian" port="-1"/>

基于 hessian 协议的 python 测试用例
# pip install python-hessian
from pyhessian.client import HessianProxy
def test_dubbo_say_hello():
url = 'http://10.1.1.207:80/org.apache.dubbo.demo.DemoService'
service = HessianProxy(url)
r = service.sayHello("seveniruby")
assert str(r).startswith("Hello seveniruby,")
r = service.sayHello("123")
assert str(r).startswith("Hello 123,")
r = service.sayHello("https://ceshiren.com")
assert str(r).startswith("Hello https://ceshiren.com,")