《Apache Dubbo微服务开发从入门到精通》——服务治理与生态——三、 网关(6) https://developer.aliyun.com/article/1223986
c) 流程分析
流程分析是从源码的角度,展示服务注册流程,数据同步流程和服务调用流程。
• 服务注册流程
。 读取dubbo服务
使用注解@ShenyuDubboClient标记需要注册到网关的dubbo服务。
注解扫描通过ApacheDubboServiceBeanListener完成,它实现了ApplicationListener接口,在Spring容器启动过程中,发生上下文刷新事件时,开始执行事件处理方法onApplicationEvent()。在重写的方法逻辑中,读取Dubbo服务ServiceBean,构建元数据对象和URI对象,并向shenyu-admin注册。
具体的注册逻辑由注册中心实现,请参考客户端接入原理。
。 处理注册信息
客户端通过注册中心注册的元数据和URI数据,在shenyu-admin端进行处理,负责存储到数据库和同步给shenyu网关。Dubbo插件的客户端注册处理逻辑在ShenyuClientRegisterDubboServiceImpl中。继承关系如下:
ShenyuClientRegisterService:客户端注册服务,顶层接口。
FallbackShenyuClientRegisterService:注册失败,提供重试操作。
AbstractShenyuClientRegisterServiceImpl:抽象类,实现部分公共注册逻辑。
ShenyuClientRegisterDubboServiceImpl:实现`Dubbo`插件的注册。
注册信息包括选择器,规则和元数据。
整体的dubbo服务注册流程如下:
• 数据同步流程
。 admin更新数据
假设在在后台管理系统中,新增一条选择器数据,请求会进入SelectorController类中的createSelector()方法,它负责数据的校验,添加或更新数据,返回结果信息。
在SelectorServiceImpl类中通过createOrUpdate()方法完成数据的转换,保存到数据库,发布事件,更新upstream。
在Service类完成数据的持久化操作,即保存数据到数据库。发布变更数据通过eventPublisher.publishEvent()完成,这个eventPublisher对象是一个ApplicationEventPublisher类,这个类的全限定名是org.springframework.context.ApplicationEventPublisher,发布数据的功能正是是通过Spring相关的功能来完成的。
当事件发布完成后,会自动进入到DataChangedEventDispatcher类中的onApplicationEvent()方法,根据不同数据类型和数据同步方式进行事件处理。
。 网关数据同步
网关在启动时,根据指定的数据同步方式加载不同的配置类,初始化数据同步相关类。
在接收到数据后,进行反序列化操作,读取数据类型和操作类型。不同的数据类型,有不同的数据处理方式,所以有不同的实现类。但是它们之间也有相同的处理逻辑,所以可以通过模板方法设计模式来实现。相同的逻辑放在抽象类AbstractDataHandler中的handle()方法中,不同逻辑就交给各自的实现类。
新增一条选择器数据,是新增操作,会进入到SelectorDataHandler.doUpdate()具体的数据处理逻辑中。
在通用插件数据订阅者CommonPluginDataSubscriber,负责处理所有插件、选择器和规则信息
将数据保存到网关的内存中,BaseDataCache是最终缓存数据的类,通过单例模式实现。选择器数据就存到了SELECTOR_MAP这个Map中。在后续使用的时候,也是从这里拿数据。
上述逻辑用流程图表示如下:
• 服务调用流程
在Dubbo插件体系中,类继承关系如下:
注:
ShenyuPlugin:顶层接口,定义接口方法。
AbstractShenyuPlugin:抽象类,实现插件共有逻辑。
AbstractDubboPlugin:dubbo插件抽象类,实现dubbo共有逻辑(ShenYu网关支持ApacheDubbo和AlibabaDubbo)。
ApacheDubboPlugin:ApacheDubbo插件。
org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()
通过ShenYu网关代理后,请求入口是ShenyuWebHandler,它实现了org.springframework.web.server.WebHandler接口,通过责任链设计模式将所有插件连接起来。
org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()
当请求到网关时,判断某个插件是否执行,是通过指定的匹配逻辑来完成。在execute()方法中执行选择器和规则的匹配逻辑。
org.apache.shenyu.plugin.global.GlobalPlugin#execute()
最先被执行的是GlobalPlugin,它是一个全局插件,在execute()方法中构建上下文信息。
org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()
接着被执行的是RpcParamTransformPlugin,它负责从http请求中读取参数,保存到exchange中,传递给rpc服务。在execute()方法中,执行该插件的核心逻辑:从exchange中获取请求信息,根据请求传入的内容形式处理参数。
org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin
然后被执行的是DubboPlugin。在doExecute()方法中,主要是检查元数据和参数。在doDubboInvoker()方法中设置特殊的上下文信息,然后开始dubbo的泛化调用。
在genericInvoker()方法中:
。 获取ReferenceConfig对象。
。 获取泛化服务GenericService对象。
。 构造请求参数pair对象。
。 发起异步的泛化调用。
通过泛化调用就可以实现在网关调用dubbo服务了。
ReferenceConfig对象是支持泛化调用的关键对象 ,它的初始化操作是在数据同步的时候完成的。
org.apache.shenyu.plugin.response.ResponsePlugin#execute()
最后被执行的是ResponsePlugin,它统一处理网关的响应结果信息。处理类型由MessageWriter决定,类继承关系如下:
注:
MessageWriter:接口,定义消息处理方法。
NettyClientMessageWriter:处理`Netty`调用结果。
RPCMessageWriter:处理`RPC`调用结果。
WebClientMessageWriter:处理`WebClient`调用结果。
Dubbo服务调用,处理结果是RPCMessageWriter。
org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()
在writeWith()方法中处理响应结果,获取结果或处理异常。
分析至此,关于Dubbo插件的源码分析就完成了,分析流程图如下:
4) 小结
本文从实际案例出发,由浅入深分析了ShenYu网关对Dubbo服务的代理过程。涉及到的主要知识点如下:
• 通过责任链设计模式执行插件。
• 使用模板方法设计模式实现AbstractShenyuPlugin,处理通用的操作类型。
• 使用单例设计模式实现缓存数据类BaseDataCache。
• 通过springboot starter即可引入不同的注册中心和数同步方式,扩展性很好。
• 通过admin支持规则热更新,方便流量管控。
• Disruptor队列是为了数据与操作解耦,以及数据缓冲。