Skip to content

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,")