Dubbo RPC MockServer设计与实现

一、MockServer需求可界面配置RPC接口的请求和响应数据调用方代码只需很少的改动就能使用RPC Mock可平滑的切换API版本预发和生产环境忽略Mock二、设计目标代码极少侵入:调用方代码只需很少的改动(例如增加一个注解),就能使用Mock调用隔离:Mock调用和真实调用隔离环境隔离:预发和生产环境下忽略掉Mock易用性:提供webUI配置API版本切换:支持依赖...
摘要由CSDN通过智能技术生成

1. MockServer需求

  • 可界面配置RPC接口的请求和响应数据
  • 调用方代码只需很少的改动就能使用RPC Mock
  • 可平滑的切换API版本
  • 预发和生产环境忽略Mock

2. 设计目标

  • 代码极少侵入:调用方代码只需很少的改动(例如增加一个注解),就能使用Mock
  • 调用隔离:Mock调用和真实调用隔离
  • 环境隔离:预发和生产环境下忽略掉Mock
  • 易用性:提供webUI配置
  • API版本切换:支持依赖的API版本切换

3. 设计思路

  • 代码极少侵入 -> 提供自定义注解声明要mock的服务
  • 调用隔离 -> 方案对比
    • 方案一 :在原有协议DubboProtocol上修改,即真实调用和Mock调用走同样的协议,占用同样的网络通道。
    • 方案二 :扩展一个新的网络传输层(Transporter),Mock调用走这个传输通道,并且在底层就把Mock数据返回,无需经过上层的Invoker和Proxy。
    • 方案三 :扩展协议(MockProtocol),扩展服务方(MockServiceConfig),扩展调用方(MockRefrenceConfig),扩展Invoker(MockInvoker),Proxy(MockProxyFactory),扩展注册协议(MockRegistry)。
  • 环境隔离 -> 注解的生效和失效机制(Guice注解的binding,spring bean的EnableAutoConfiguration)
  • API版本切换 -> classloader隔离机制

4. 实现

4.1 无米之炊?–当MockServer遇上ProtoBuf

protobuf的请求响应类都是根据事先定义好的.proto描述文件生成的,而MockServer没有这些描述文件,也就没有那些生成的请求类响应类。收到的请求数据也就不知道怎么解码,如何解决?

  • 用户上传.proto文件? – 缺点:.proto文件不开放,调用方要去问被调用方拿到这个文件,不方便,实施难。
  • 用户填写Maven Artifact信息(GroupId,ArtifactId,Version)? – 优点:Maven Artifact是开放的,获取容易。

有了Maven Artifact之后,就可动态下载Maven jar,把里面的类加载到MockServer的上下文中。

  • Eclipse aether 项目 (动态下载maven包)
  • org.reflections.Reflections项目 (类扫描)

4.2 dubbo现有的方案能满足MockServer的需求吗?

dubbo(2.6.4版本)有一个支持MockServer的方案:泛化调用。

下面是dubbo文档中泛化调用的示例,可以看到需要显式调用**$invoke** 方法,对代码有较大的侵入。所以不考虑泛化的方案

GenericService barService = (GenericService) applicationContext.getBean("barService");
Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });

4.3 利用Dubbo丰富的SPI扩展来实现Mock

下图来自dubbo官方文档,是暴露服务的时序图

在这里插入图片描述

从中可以看到export一个服务时,经过的关键类:

  1. ServiceConfig :服务配置类
  2. ProxyFactory :代理工厂类
  3. Invoker :这里是服务端的Invoker,由ProxyFactory提供
  4. Protocol :RPC 协议,封装远程调用细节
  5. Registry : 注册中心

这些关键类也是我们要扩展的类。扩展好后,通过ServiceConfig来指定对应的Protocol,Proxy,Registry。如下是ServiceConfig的配置代码

	ServiceConfig<Object> serviceConfig = new MockServiceConfig<>(classLoader);
	serviceConfig.setInterface(interfaceClass);
	serviceConfig.setRef(ref);
	serviceConfig.setVersion(version);
	serviceConfig.setGroup(group);
	serviceConfig.setProtocol(exportMockProtocolConfig());
	serviceConfig.setRegistry(exportMockRegistryConfig());
	serviceConfig.setApplication(exportApplicationConfig());
	serviceConfig.setProxy(getConfigValue("dubbo.service.proxy"));
	serviceConfig.setFilter(getConfigValue("dubbo.service.filter"));
	return serviceConfig;

4.4 用ClassLoader隔离来实现API版本切换

  • ClassLoader隔离才能做到API版本切换

    假设依赖的API包版本要升级,其中某些类有变化。MockServer需要在不重启的情况下重新加载这些类(也就是Class热替换)。如果一个Class被一个ClassLoader加载过,那么这个Class就不能再被同一个ClassLoader再次加载(指的是重新加载字节码、解析、验证)。要实现热替换,需要用一个新的classLoader去加载这个class。在我们这里,更新一个版本时MockServer会先下载一个新的maven jar包,再用一个新的URLClassLoader来加载jar包里的类。(这说明每个Maven Artifact (Maven 坐标)都有一个独立ClassLoader与之对应)。

  • 原生Dubbo不支持使用指定的ClassLoader去加载类

    由上分析,当API版本切换时,使用新的ClassLoader去加载类。但是dubbo在暴露服务时只会使用SystemClassLoader(sun.misc.Launcher$AppClassLoader)来加载类,不允许指定。如下为ServiceConfig类的doExport方法的代码段:

        try {
         
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .</
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值