为了账号安全,请及时绑定邮箱和手机立即绑定

基于动态代理 Mock Dubbo 服务的实现方案

标签:
Oracle

序言

背景概述

公司目前 Java 项目提供服务都是基于 Dubbo 框架的,而且 Dubbo 框架已经成为大部分国内互联网公司选择的一个基础组件。

在日常项目协作过程中,其实会碰到服务不稳定、不满足需求场景等情况,很多开发都会通过在本地使用 Mocktio 等单测工具作为自测辅助。那么,在联调、测试等协作过程中怎么处理?

其实,Dubbo 开发者估计也是遇到了这样的问题,所以提供了一个提供泛化服务注册的入口。但是在服务发现的时候有个弊端,就说通过服务发现去请求这个 Mock 服务的话,在注册中心必须只有一个服务有效,否则消费者会请求到其他非Mock服务上去。

为了解决这个问题,Dubbo 开发者又提供了泛化调用的入口。既支持通过注册中心发现服务,又支持通过 IP+PORT 去直接调用服务,这样就能保证消费者调用的是 Mock 出来的服务了。

以上泛化服务注册和泛化服务调用结合起来,看似已经是一个闭环,可以解决 Dubbo 服务的 Mock 问题。但是,结合日常工作使用时,会出现一些麻烦的问题:

服务提供方使用公用的注册中心,消费方无法准确调用

消费者不可能更改代码,去直连 Mock 服务

使用私有注册中心能解决以上问题,但是 Mock 最小纬度为 Method,一个 Service 中被 Mock 的 Method 会正常处理,没有被 Mock 的 Method 会异常,导致服务方需要 Mock Service 的全部方法

在解决以上麻烦的前提下,为了能快速注册一个需要的 Dubbo 服务,提高项目协作过程中的工作效率,开展了 Mock 工厂的设计与实现。

功能概述

Mock Dubbo 服务

单个服务器,支持部署多个相同和不同的 Service

动态上、下线服务

非 Mock 的 Method 透传到基础服务

一、方案探索

1.1 基于 Service Chain 选择 Mock 服务的实现方式

1.1.1 Service Chain 简单介绍

在业务发起的源头添加 Service Chain 标识,这些标识会在接下来的跨应用远程调用中一直透传并且基于这些标识进行路由,这样我们只需要把涉及到需求变更的应用的实例单独部署,并添加到 Service Chain 的数据结构定义里面,就可以虚拟出一个逻辑链路,该链路从逻辑上与其他链路是完全隔离的,并且可以共享那些不需要进行需求变更的应用实例。根据当前调用的透传标识以及 Service Chain 的基础元数据进行路由,路由原则如下:

当前调用包含 Service Chain 标识,则路由到归属于该 Service Chain 的任意服务节点,如果没有归属于该

Service Chain 的服务节点,则排除掉所有隶属于 Service Chain 的服务节点之后路由到任意服务节点

当前调用没有包含 Service Chain 标识,则排除掉所有隶属于 Service Chain 的服务节点之后路由到任意服务节点

当前调用包含 Service Chain 标识,并且当前应用也属于某个 Service Chain 时,如果两者不等则抛出路由异常

以 Dubbo 框架为例,给出了一个 Service Chain 实现架构图(下图来自有赞架构团队)

webp



1.1.2 Mock 服务实现设计方案

方案一、基于 GenericService 生成需要 Mock 接口的泛化实现,并注册到 ETCD 上(主要实现思路如下图所示)。

webp



方案二、使用 Javassist,生成需要mock接口的Proxy实现,并注册到 ETCD 上(主要实现思路如下图所示)。

webp



1.1.3 设计方案比较

方案一优点:实现简单,能满足mock需求

继承 GenericService,只要实现一个$invoke( String methodName, String[]parameterTypes, Object[]objects ),可以根据具体请求参数做出自定义返回信息。

接口信息只要知道接口名、protocol 即可。

即使该服务已经存在,也能因为 generic 字段,让消费者优先消费该 mock service。

缺点:与公司的服务发现机制冲突

由于有赞服务背景,在使用 Haunt 服务发现时,是会同时返回正常服务和带有 Service Chain 标记的泛化服务,所以必然存在两种类型的服务。导致带有 Service Chain 标记的消费者在正常请求泛化服务时报 no available invoke。

例:注册了 2个 HelloService:

正常的 :generic=false&interface=com.alia.api.HelloService&methods=doNothing,say,age

泛化的:generic=true&interface=com.alia.api.HelloService&methods=*

在服务发现的时候,RegistryDirectory 中有个 map,保存了所有 Service 的注册信息。也就是说, method=* 和正常 method=doNothing,say,age 被保存在了一起。

webp



客户端请求服务的时候,优先匹配到正常的服务的 method,而不会去调用泛化服务。

导致结果:访问时,会跳过 genericFilter,报 no available invoke。

方案二优点:Proxy 实现,自动生成一个正常的 Dubbo 接口实现

1.Javassist 有现成的方法生成接口实现字节码,大大简化了对用户代码依赖。例如:

返回 String、Json 等,对单 method 的 mock 实现,都无需用户上传实现类。

透传时统一由平台控制,不配置 mock 的方法默认就会进行透传,而且保留 Service Chain 标记。

2.Mock 服务注册 method 信息完整。

3.生成接口 Proxy 对象时,严格按照接口定义进行生成,返回数据类型有保障。

缺点:

无优先消费选择功能。

字节码后台生成,不利于排查生成的 Proxy 中存在问题。

1.1.4 选择结果

由于做为平台,不仅仅需要满足 mock 需求,还需要减少用户操作,以及支持现有公司服务架构体系,所以选择设计方案二。

1.2 基于动态代理结合 ServiceConfig 实现动态上、下线服务

1.2.1 Dubbo 暴露服务的过程介绍

webp



上图(来自 dubbo 开发者文档)暴露服务时序图: 首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:StudentInfoServiceImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例。到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程,Exporter 会通过转化为 URL 的方式暴露服务。 从 dubbo 源码来看,dubbo 通过 Spring 框架提供的 Schema 可扩展机制,扩展了自己的配置支持。dubbo-container 通过封装 Spring 容器,来启动了 Spring 上下文,此时它会去解析 Spring 的 bean 配置文件(Spring 的 xml 配置文件),当解析 dubbo:service 标签时,会用 dubbo 自定义 BeanDefinitionParser 进行解析。dubbo 的 BeanDefinitonParser 实现为 DubboBeanDefinitionParser。 

Spring.handlers 文件:http://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler


public

class

DubboNamespaceHandler

extends

NamespaceHandlerSupport

{


public

DubboNamespaceHandler

() {

     }


public

void

init() {


this

.registerBeanDefinitionParser(

"application"

,

new

DubboBeanDefinitionParser

(

ApplicationConfig

.

class

,

true

));


this

.registerBeanDefinitionParser(

"module"

,

new

DubboBeanDefinitionParser

(

ModuleConfig

.

class

,

true

));


this

.registerBeanDefinitionParser(

"registry"

,

new

DubboBeanDefinitionParser

(

RegistryConfig

.

class

,

true

));



作者:java菜
链接:https://www.jianshu.com/p/18f7c93e3d8a


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消