京东质量社区 基于 Dubbo 分布式服务框架的一种 MOCK 方式

taki for 京东 · March 13, 2017 · Last by taki replied at October 25, 2021 · 4286 hits

首先简单说一下 Dubbo 是什么呢?不懂的同学百度一下去吧!

Dubbo 目前的应用已经越来越广泛、或者基于 Dubbo 二次开发的也越来越多,那么应用到 Dubbo 的系统基本也是采用微服务架构设计的系统、或者多个系统、多个应用之间的接口是有依赖关系的,所以就会出现需要 MOCK 的应用场景。具体什么是 MOCK,不懂的同学也百度一下吧!

在 Dubbo 里面有两个概念:
1:Provider 暴露服务的服务提供方 (接口提供方)
2:Consumer 调用远程服务的服务消费方 (接口调用方)


下面是应用 A 依赖应用 B 的 UserService 接口,在工程里面代码的配置
Dubbo 的应用是以 spring 注入服务,首先应用 B 要发布一个接口
一、应用 B 发布一个 UserService 接口
1.首先有一个 UserServce 接口类

package com.test.service;
public interface UserService{
  public User getUser(User user);
}

2.来一个 DTO 的 User 类

public class User{
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

3.接口类和参数类创建好之后,通过 spring 发布接口,接口发布到 zookeeper 里面,我这里没有写接口的实现,这里用不到

<!-- 提供方应用名称信息,这个相当于起一个名字,我们dubbo管理页面比较清晰是哪个应用暴露出来的 -->
 <dubbo:application name="dubbo_provider"></dubbo:application>
 <!-- 使用zookeeper注册中心暴露服务地址 -->  
 <dubbo:registry address="zookeeper://127.0.0.1:2181" check="false" subscribe="false" register=""></dubbo:registry>
<!-- 要暴露的服务接口 -->  
<dubbo:service interface="com.test.service.UserService" ref="userService" />      

二、应用 A 引用应用 B 发布的 UserService 接口
1.dubbo 服务框架要求接口调用方和接口提供方的 java 接口文件和参数类型保持一直,所以我们需要服务端提供发布的接口类和参数类,通常服务端会通过 Maven 发布接口的 Jar 包共服务调用方 (这里的应用 A) 使用,并且参数类要进行序列化可以,入参通过序列化之后到服务端在反序列化,序列化这块大家百度不细说
2.假设我们的应用 A 引入的应用 A 提供的 jar 包
3.通过 spring 注册接口引用的接口

<dubbo:application name="dubbo_consumer"></dubbo:application>
  <!-- 使用zookeeper注册中心暴露服务地址 -->  
  <dubbo:registry address="zookeeper://192.168.74.129:2181" check="false"></dubbo:registry> 
    <!-- 要引用的服务 -->  
  <dubbo:reference interface="com.test.service.UserService" id="userService"></dubbo:reference>

两个应用都完事了,那么我们的应用场景是 MOCK 应用 B(接口提供方),通常的 MOCK 方式是,测试人员自己发布一个一样的接口 (应用 C),然后把应用 A 的调用指向(应用 C),这种方式可以解决 MOCK

这种方式的弊端:

1.需要开发 MOCK 代码

2.对人员要求技能高

3.需要接口提供方的依赖 jar

4.需要容器发布应用 C

5.接口变更需要更新 MOCK 代码

6.发布时间长,需要编码发布


那么我们采取另外一个方式,filter 过滤器,filter 是放在客户端的 (应用 A),通过拦截请求,如何配置 filter 参考http://blog.csdn.net/mj158518/article/details/47379799
配置 filter 之后呢应用 A 的请求首先会到 filter 里面

public class CustomConsumerFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
      Result result = null; //首先定义一个返回对象;
      String interfaceName = invoker.getUrl().getPath();     //获取接口名称
      String methodName = invocation.getMethodName(); //获取方法名称
      Object[] arguments =invocation.getArguments();        //获取参数,我举的例子是一个参数,实际可能有多个参数,多种类型,
      String paramJson =JSON.toJSONString(arguments, SerializerFeature.WriteClassName);//这里面可能有泛型,所以入参在序列化时要上json
      List<String> paramTypes = reflectTools.getMethodParameterType(interfaceName, methodName);     //获取所有的入参类型,这块要自己写反射处理
      String methodReturnType = reflectTools.getMethodTypeReturnString(interfaceName, methodName);  //获取被调用方法返回类型,这块要自己写反射处理
      //上面两行可以拿到入参类型和返回值的类型,是因为我们在客户端只用里面有参数类

      //下面定义一个httpclient,用于发送Http请求,首先我们发送一个请求,确认这个接口的这个方法是否在我们测试平台存在了
       Map paramMap = new HashMap();
       paramMap.put("interfaceName", interfaceName);
       paramMap.put("methodName", methodName);
       String r = requestClient.send(paramMap, "getInterfaceInfo");     
      //如果不存在,调用真实的接口获取返回值,
      result = invoker.invoke(invocation);   //调用真实接口
      //然后把接口信息存在我们的测试平台

  }
}

具体的代码我就不写了,下面弄个逻辑图吧,里面各种类型处理,用户选择匹配 MOCK 返回数据等,太多了代码


测试平台,用户选择匹配

测试平台,用户配置 MOCK 返回数据,这里面的关键字,决定用户需要什么样的返回数据,MOCK 数据不是死的是可以配置的


这样做的好处:

1.测试人员不需要编写测试代码

2.人员要求低

3.不需要依赖 jar

4.无需部署发布

5.不需要维护接口信息

6.用户配置秒级生效


那么大家用这种方式会发现,需要把我们的 filter 包放到应用 A 里面,并且配置 spring 的接口文件,相信大家都有自动部署,可以在部署过程中,直接扫描 spring.xml 找出接口,默认全部加上 filter,同时把我们的 filter 包也放到应用的 lib 下,做到用户不感知,达到自动 MOCK 的生态


上面细节的东西不是很多,主要是讲一种思路,这个文章懂 dubbo 的人可能更容易理解,欢迎大家讨论

附言 1  ·  October 22, 2017

https://testerhome.com/topics/10481 参考 dubbo 接口测试技术

共收到 26 条回复 时间 点赞

贾老板的实践不错。
关于 filter,可以用 netfilx Zuul 做通用 gateway 过滤。

taki #2 · March 24, 2017 Author
卡农Lucas 回复

本身 dubbo 是一个运行中的应用,难点在于不嵌入研发的代码,还要拦截住请求,并且不能设置代理之类的,netfilx Zuul,做不了吧

能否做到,任何 dubbo 代码应用在调用任何 service 的时候,都去测试平台去检查一下这个 service 是否要走 mock?

taki #4 · April 23, 2017 Author
shine 回复

可以

taki 回复

这里的实现方式是要入侵应用 A 吗?A 打包的时候,里面加多这里的 filter 实现。
有没有不入侵任何应用的实现方式呢?

taki #6 · April 24, 2017 Author
shine 回复

不配置的话,拦截不到的

dubbo 的 mock 机制,用于服务降级,其稳定性是否可靠?

请问有没有开源的地址,想参考一下。

taki 回复

配置 filter 会改动开发的代码,开发一般不太愿意配合,这个你们是怎么协调开发配合的呢?

taki #10 · August 14, 2017 Author
crying_Dream 回复

filter 并不需要动他们的代码,无入侵的

taki 回复

你说的无入侵是什么意思?我理解 CustomConsumerFilter 类不是要放到应用 A 中吗,这个不就相当于让开发动他们的项目代码吗?

taki #12 · August 14, 2017 Author
crying_Dream 回复

filter 是我们做的 jar,在部署的时候扔到 lib 下面,filter 里面每什么逻辑就是一个 http 请求,把接口名、方法名、参数类型、入参带到平台,然后去 mock 平台拿返回的 json,在反序列化

taki 回复

假设两个人 QA1、QA2 并行对 A、B 这个服务模块测试,QA1 测试时开启了服务 B 接口 mock,但是 QA2 测试时不想 mock 掉服务 B 接口==》这种场景下怎么保证 QA1、QA2 测试互不干扰呢

楼主大大,能否详细介绍下如何实现 mock 数据反序列化的么?

非常感谢分享,解决了大问题。

taki #16 · November 22, 2018 Author
crying_Dream 回复

服务正常为什么还需要 mock 呢?

楼主你好,请问 reflectTools.getMethodParameterType 方法需要自己写吧?

taki #18 · August 11, 2019 Author

是的

测试平台的配置只是一个提供配置 filter 内部过滤请求逻辑的条件用的吗?还有
,测试平台配置的出参就是用来做返回值的吗?

taki #20 · September 29, 2019 Author
糊涂hutoo 回复

有 mock 逻辑的 ,不是单纯的使用静态数据

楼主大大,可以介绍下怎么实现 mock 数据反序列化吗?看到这里有些困惑

taki 回复

你好请问做成 jar 包的形式引入,也需要修改开发的 pom.xml 文件吧,具体方式您可以再讲一下吗

请问有 github 代码吗?

taki #24 · March 17, 2020 Author
ganhaiying 回复

没有

大佬 ,请教下, 这东西 测试环境搞,怎么避免放到线上去。 例如我们是开发代码里面 搞了 Filter ,但是毕竟会影响速度等, 在避免这个功能跑到线上去,大佬有什么建议么?

taki #26 · October 25, 2021 Author

分支部署隔离就好了

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up