使用了Dubbo这么久了,今天来剖析架构实现,在此做个记录,也与大家共享。
在分析之前,先同步个概念:SPI,我认为它是Dubbo架构中很重要的一个功能点。在官网中有提到这样一句话:
采用 Microkernel + Plugin 模式,Microkernel 只负责组装 Plugin,Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
SPI就是实现扩展的利器。
SPI
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。
分层架构
本文以分层为切入点,分析Dubbo架构,后面的文章是以分层的思路来进行!
说到分层,首先想到的就是网络分层。
众所周知,网络的分层优点有很多:
- 各层之间是独立的。 某一层并不需要知道它下一层是如何实现的,而仅仅需要知道该层通过层间的接口所提供的服务。由于每一层只实现一种相对独立的功能,因而可以将一个难以处理的复杂问题分解为若干个较容易处理的更小问题,这样,整个问题的复杂度就下降了。
- 灵活性好。 当任何一层发生变化时,只要层间接口关系保持不变,则在这层以上或以下各层均不受影响,此外,对某一层提供的服务还可以进行修改。当某层提供的服务不再需要时,甚至可以将这层取消。
- 结构上可分割开。 各层都可以采用最合适的技术来实现。
- 易于实现和维护。 这种结构使得实现和调试一个庞大而又复杂的系统变得易于处理,因为整个系统已被分解为若干个相对独立的子系统。
- 能促进标准化工作。 因为每一层的功能及其所提供的服务都已有了精确的说明。
如果我们根据分层的思想设计一个RPC,我们要分几层?每层的任务是什么呢?
如果让我设计一个最简单的远程调用的框架,我应该实现什么样的功能呢?我想无非就是构建请求数据,发送请求数据,接收返回数据。那好,我们开始设计:
- 要有发送接收机制,规定发送和接收的形式。好的,那么这就对应dubbo的一个层专门做这个事。(protocol 远程调用层)
- 发送机制有了,我要真正的传输数据啊,解决怎么把数据通过网络发出去。好的,那么这又是一层,发数据的。(transport 网络传输层)
- 发送和接收数据了,可是数据的格式是什么样子的呢,java对象直接是发不出去的?好的,这又是一层,数据序列化的。(serialize 数据序列化层)
- 数据也发出去了,那我统计发送接收的次数和耗时等等。好的,这又是一层),监控调用。(monitor 监控层)
- 那么我现在把调用的功能实现了。可是我想知道,供我调用的系统有哪些,和我一起工作的系统有哪些。那么又一层,我们暂且称为获取目标层。(registry 注册中心层)
- 接着上面的思路聊,假如有好多相同功能的系统我可以调用,我该选哪一个呢?好的,路由层来了。(cluster 路由层)
那好,我们设计完了。其实我说的就是Dubbo的思路。以上每一层会封装对外实现的接口,供SPI机制的具体实现,并且还可以动态的配置。那么问题来了,在哪设置呢?
-
为了做上面的工作,那就加一个配置层专门处理这些配置。(config 配置层)
-
刚才说了那么多,是不是用起来很麻烦啊。确实很麻烦,不过易用性是一个框架提高生产效率的必备条件吧。那么就加一个代理层吧,能让我们像调用本地方法一样调用。(proxy 服务代理层)
在分层的场景下,就把一个很复杂的调用过程分解开了。每一层专注本层,并暴露各层间的接口。
Dubbo官网的架构图
初看上图错综复杂,但是按着某条思路看来就很有条理性。先不在意那些细节,条啊,框啊,括号之类的,在此只关注层级:
-
config 配置层: 对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
-
proxy 服务代理层: 服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
-
registry 注册中心层: 封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
-
cluster 路由层: 封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
-
monitor 监控层: RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
-
protocol 远程调用层: 封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
-
exchange 信息交换层: 封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
-
transport 网络传输层: 抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
-
serialize 数据序列化层: 可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool
需要特别说明的点:
- 在 RPC 中,Protocol 是核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。
- 而 Cluster 是外围概念,所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker,这样其它人只要关注 Protocol 层 Invoker 即可,加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响,因为只有一个提供者时,是不需要 Cluster 的。
- Proxy 层封装了所有接口的透明化代理,而在其它层都以 Invoker 为中心,只有到了暴露给用户使用时,才用 Proxy 将 Invoker 转成接口,或将接口实现转成 Invoker,也就是去掉 Proxy 层 RPC 是可以 Run 的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
- 而Remoting 实现是 Dubbo 协议的实现,如果你选择 RMI 协议,整个 Remoting 都不会用上,Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层,Transport 层只负责单向消息传输,是对 Mina, Netty, Grizzly 的抽象,它也可以扩展 UDP 传输,而 Exchange 层是在传输层之上封装了 Request-Response 语义。
看下Dubbo3.0的项目结构
Dubbo的架构我们是了解清楚了,那么后面我将会出文章,展现出各层的功能实现。
模块分包图
- dubbo-cluster 集群模块:将多个服务提供方伪装为一个提供方,包括:负载均衡, 容错,路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。
- dubbo-common 公共逻辑模块:包括 Util 类和通用模型。
- dubbo-remoting 远程通讯模块:相当于 Dubbo 协议的实现,如果 RPC 用 RMI协议则不需要使用此包。
- dubbo-rpc 远程调用模块:抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。
- dubbo-registry 注册中心模块:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
- dubbo-monitor 监控模块:统计服务调用次数,调用时间的,调用链跟踪的服务。
- dubbo-config 配置模块:是 Dubbo 对外的 API,用户通过 Config 使用Dubbo,隐藏 Dubbo 所有细节。
- dubbo-container 容器模块:是一个 Standlone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。
剖析各层各模块
下面我的主要任务就在剖析各层各模块的源码,看看Dubbo是怎么实现的。
- Dubbo3.0最新源码分析:protocol 远程调用层
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位
- 坑位