天天看点

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

我们先从下图开始简单介绍Dubbo分层设计概念:

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

(引用自Duboo开发指南-框架设计文档)

如图描述Dubbo实现的RPC整体分10层:service、config、proxy、registry、cluster、monitor、protocol、exchange、transport、serialize。

service:使用方定义的接口和实现类; config:负责解析Dubbo定义的配置,比如注解和xml配置,各种参数; proxy:主要负责生成消费者和提供者的代理对象,加载框架功能,比如提供者过滤器链,扩展点; registry:负责注册服务的定义和实现类的装载; cluster:只有消费者有这么一层,负责包装多个服务提供者成一个‘大提供者’,加载负载均衡、路有等扩展点; monitor:定义监控服务,加载监控实现提供者; protocol:封装RPC调用接口,管理调用实体的生命周期; exchange:封装请求响应模式,同步转异步; transport:抽象传输层模型,兼容netty、mina、grizzly等通讯框架; serialize:抽象序列化模型,兼容多种序列化框架,包括:fastjson、fst、hessian2、kryo、kryo2、protobuf等,通过序列化支持跨语言的方式,支持跨语言的rpc调用;

Dubbo这么分层的目的在于实现层与层之间的解耦,每一层都定义了接口规范,也可以根据不同的业务需求定制、加载不同的实现,具有极高的扩展性。

接下来结合上图简单描述一次完整的rpc调用过程:

从Dubbo分层的角度看,详细时序图如下,蓝色部分是服务消费端,浅绿色部分是服务提供端,时序图从消费端一次Dubbo方法调用开始,到服务端本地方法执行结束。

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

从Dubbo核心领域对象的角度看,我们引用Dubbo官方文档说明,如下图所示。Dubbo核心领域对象是Invoker,消费端代理对象是proxy,包装了Invoker的调用;服务端代理对象是一个Invoker,他通过exporter包装,当服务端接收到调用请求后,通过exporter找到Invoker,Invoker去实际执行用户的业务逻辑。

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

(引用自Dubbo官方文档)

下图出自开发指南-框架设计-引用服务时序,主要流程是:从注册中心订阅服务提供者,然后启动tcp服务连接远端提供者,将多个服务提供者合并成一个Invoker,用这个Invoker创建代理对象。

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

下图出自开发指南-框架设计-暴露服务时序,主要流程是:创建本地服务的代理Invoker,启动tcp服务暴露服务,然后将服务注册到注册中心。

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

接下来我们结合Dubbo服务的注册和发现,从配置层开始解释每一层的作用和原理。

示例服务接口定义如下:

配置层提供配置处理工具类,在容器启动的时候,通过ServiceConfig.export实例化服务提供者,ReferenceConfig.get实例化服务消费者对象。

Dubbo应用使用spring容器启动时,Dubbo服务提供者配置处理器通过ServiceConfig.export启动Dubbo远程服务暴露本地服务。Dubbo服务消费者配置处理器通过ReferenceConfig.get实例化一个代理对象,并通过注册中心服务发现,连接远端服务提供者。

Dubbo配置可以使用注解和xml两种形式,本文采用注解的形式进行说明。

Spring容器启动过程中,填充bean属性时,对含有Dubbo引用注解的属性使用org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor进行初始化。如下是ReferenceAnnotationBeanPostProcessor的构造方法,Dubbo服务消费者注解处理器处理以下三个注解:DubboReference.class、Reference.class、com.alibaba.dubbo.config.annotation.Reference.class修饰的类。

ReferenceAnnotationBeanPostProcessor类定义:

Dubbo服务发现到这一层,Dubbo即将开始构建服务消费者的代理对象,CouponServiceViewFacade接口的代理实现类。

Spring容器启动的时候,加载注解@org.apache.dubbo.config.spring.context.annotation.DubboComponentScan指定范围的类,并初始化;初始化使用dubbo实现的扩展点org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor。

ServiceClassPostProcessor处理的注解类有DubboService.class,Service.class,com.alibaba.dubbo.config.annotation.Service.class。

如下是ServiceClassPostProcessor类定义:

等待Spring容器ContextRefreshedEvent事件,启动Dubbo应用服务监听端口,暴露本地服务。

Dubbo服务注册到这一层,Dubbo即将开始构建服务提供者的代理对象,CouponServiceViewFacade实现类的反射代理类。

为服务消费者生成代理实现实例,为服务提供者生成反射代理实例。

CouponServiceViewFacade的代理实现实例,消费端在调用query方法的时候,实际上是调用代理实现实例的query方法,通过他调用远程服务。

CouponServiceViewFacade的反射代理实例,服务端接收到请求后,通过该实例的Invoke方法最终执行本地方法query。

Dubbo代理工厂接口定义如下,定义了服务提供者和服务消费者的代理对象工厂方法。服务提供者代理对象和服务消费者代理对象都是通过工厂方法创建,工厂实现类可以通过SPI自定义扩展。

默认采用Javaassist代理工厂实现,Proxy.getProxy(interfaces)创建代理工厂类,newInstance创建具体代理对象。

Dubbo为每个服务消费者生成两个代理类:代理工厂类,接口代理类。

CouponServiceViewFacade代理工厂类:

最终生成的CouponServiceViewFacade的代理对象如下,其中handler的实现类是InvokerInvocationHandler,this.handler.invoke方法发起Dubbo调用。

默认Javaassist代理工厂实现,使用Wrapper包装本地服务提供者。proxy是实际的服务提供者实例,即CouponServiceViewFacade的本地实现类,type是接口类定义,URL是injvm协议URL。

Dubbo为每个服务提供者的本地实现生成一个Wrapper代理类,抽象Wrapper类定义如下:

具体Wrapper代理类使用字节码技术动态生成,本地服务CouponServiceViewFacade的代理包装类举例:

在服务初始化流程中,服务消费者代理对象生成后初始化就完成了,服务消费端的初始化顺序:ReferenceConfig.get->从注册中心订阅服务->启动客户端->创建DubboInvoker->构建ClusterInvoker→创建服务代理对象;

而服务提供端的初始化才刚开始,服务提供端的初始化顺序:ServiceConfig.export->创建AbstractProxyInvoker,通过Injvm协议关联本地服务->启动服务端→注册服务到注册中心。

接下来我们讲注册层。

封装服务地址的注册与发现,以服务 URL 为配置中心。服务提供者本地服务启动成功后,监听Dubbo端口成功后,通过注册协议发布到注册中心;服务消费者通过注册协议订阅服务,启动本地应用连接远程服务。

注册协议URL举例:

zookeeper://xxx/org.apache.dubbo.registry.RegistryService?application=xxx&...

注册服务工厂接口定义如下,注册服务实现通过SPI扩展,默认是zk作为注册中心。

注册服务接口定义;

服务消费方从注册中心订阅服务提供者后,将多个提供者包装成一个提供者,并且封装路由及负载均衡策略;并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance;

服务提供端不存在集群层。

集群领域主要负责将多个服务提供者包装成一个ClusterInvoker,注入路由处理器链和负载均衡策略。主要策略有:failover、failfast、failsafe、failback、forking、available、mergeable、broadcast、zone-aware。

集群接口定义如下,只有一个方法:从服务目录中的多个服务提供者构建一个ClusterInvoker。

作用是对上层-代理层屏蔽集群层的逻辑;代理层调用服务方法只需执行Invoker.invoke,然后通过ClusterInvoker内部的路由策略和负载均衡策略计算具体执行哪个远端服务提供者。

ClusterInvoker执行逻辑,先路由策略过滤,然后负载均衡策略选择最终的远端服务提供者。示例代理如下:

服务目录接口定义如下,Dubbo方法接口调用时,将方法信息包装成invocation,通过Directory.list过滤可执行的远端服务。

通过org.apache.dubbo.registry.integration.RegistryDirectory桥接注册中心,监听注册中心的路由配置修改、服务治理等事件。

从已知的所有服务提供者中根据路由规则刷选服务提供者。

服务订阅的时候初始化路由处理器链,调用远程服务的时候先使用路由链过滤服务提供者,再通过负载均衡选择具体的服务节点。

路由处理器链工具类,提供路由筛选服务,监听更新服务提供者。

订阅服务的时候,将路由链注入到RegistryDirectory中;

根据不同的负载均衡策略从可使用的远端服务实例中选择一个,负责均衡接口定义如下:

监控RPC调用次数和调用时间,以Statistics为中心,扩展接口为 MonitorFactory, Monitor, MonitorService。

监控工厂接口定义,通过SPI方式进行扩展;

监控服务接口定义如下,定义了一些默认的监控维度和指标项;

通过过滤器的方式收集服务的调用次数和调用时间,默认实现:

org.apache.dubbo.monitor.dubbo.DubboMonitor。

封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter。

接下来介绍Dubbo RPC过程中的常用概念:

1)Invocation是请求会话领域模型,每次请求有相应的Invocation实例,负责包装dubbo方法信息为请求参数; 2)Result是请求结果领域模型,每次请求都有相应的Result实例,负责包装dubbo方法响应; 3)Invoker是实体域,代表一个可执行实体,有本地、远程、集群三类; 4)Exporter服务提供者Invoker管理实体; 5)Protocol是服务域,管理Invoker的生命周期,提供服务的暴露和引用入口;

服务初始化流程中,从这一层开始进行远程服务的暴露和连接引用。

对于CouponServiceViewFacade服务来说,服务提供端会监听Dubbo端口启动tcp服务;服务消费端通过注册中心发现服务提供者信息,启动tcp服务连接远端提供者。

协议接口定义如下,统一抽象了不同协议的服务暴露和引用模型,比如InjvmProtocol只需将Exporter,Invoker关联本地实现。DubboProtocol暴露服务的时候,需要监控本地端口启动服务;引用服务的时候,需要连接远端服务。

Invoker接口定义

Invocation是RPC调用的会话对象,负责包装请求参数;Result是RPC调用的结果对象,负责包装RPC调用的结果对象,包括异常类信息;

服务暴露的时候,开启RPC服务端;引用服务的时候,开启RPC客户端。

接收响应请求;

调用远程服务;

封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer。

使用request包装Invocation作为完整的请求对象,使用response包装result作为完整的响应对象;Request、Response相比Invocation、Result添加了Dubbo的协议头。

交换器对象接口定义,定义了远程服务的绑定和连接,使用SPI方式进行扩展;

交换层模型类图:

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

服务提供端接收到请求后,本地执行,发送响应结果;

服务消费端发起请求的封装,方法执行成功后,返回一个future;

抽象传输层模型,兼容netty、mina、grizzly等通讯框架。

传输器接口定义如下,它与交换器Exchanger接口定义相似,区别在于Exchanger是围绕Dubbo的Request和Response封装的操作门面接口,而Transporter更加的底层,Exchanger用于隔离Dubbo协议层和通讯层。

自定义传输层模型

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

通过SPI的方式,动态选择具体的传输框架,默认是netty;

netty框架的channel适配如下,采用装饰模式,使用netty框架的channel作为Dubbo自定义的channel做实现;

抽象序列化模型,兼容多种序列化框架,包括:fastjson、fst、hessian2、kryo、kryo2、protobuf等,通过序列化支持跨语言的方式,支持跨语言的RPC调用。

定义Serialization扩展点,默认hessian2,支持跨语言。Serialization接口实际是一个工厂接口,通过SPI扩展;实际序列化和反序列化工作由ObjectOutput,ObjectInput完成,通过装饰模式让hessian2完成实际工作。

下图出自开发指南-实现细节-远程通讯细节,描述Dubbo协议头设计;

源码解读Dubbo分层设计思想一、Dubbo分层整体设计概述二、配置层三、 代理层四、注册层五、集群层六、监控层七、协议层八、交换层九、传输层十、序列化十一、总结

0-15bit表示Dubbo协议魔法数字,值:0xdabb;

16bit请求响应标记,Request - 1; Response - 0;

17bit请求模式标记,只有请求消息才会有,1表示需要服务端返回响应;

18bit是事件消息标记,1表示该消息是事件消息,比如心跳消息;

19-23bit是序列化类型标记,hessian序列化id是2,fastjson是6,详见org.apache.dubbo.common.serialize.Constants;

24-31bit表示状态,只有响应消息才有用;

32-64bit是RPC请求ID;

96-128bit是会话数据长度;

128是消息体字节序列;

Dubbo将RPC整个过程分成核心的代理层、注册层、集群层、协议层、传输层等,层与层之间的职责边界明确;核心层都通过接口定义,不依赖具体实现,这些接口串联起来形成了Dubbo的骨架;这个骨架也可以看作是Dubbo的内核,内核使用SPI 机制加载插件(扩展点),达到高度可扩展。

vivo互联网服务器团队-Wang Genfu