Dubbo 源码解析——项目结构梳理
什么是 Dubbo
在 Dubbo 的官网上有一句非常直接的话:Apache Dubbo是一款高性能、轻量级基于Java的RPC开源框架。
什么是RPC
RPC 全称为 Remote Procedure Call Protocol 翻译过来就是远程调用协议,既然是协议那么就会有很多的实现方式。最通俗点来讲就是两个人离着很远想要打电话,那么手机拨号-呼叫-回应的过程就可以理解为是一种RPC的协议
Dubbo 就是一个应用非常广泛的 RPC 协议实现框架。Dubbo 的特点分别有连通性、健壮性、伸缩性及升级性。特点的详细介绍也可以参照官方文档,这里就不一一讲解了。
- Dubbo 的官方文档 dubbo.apache.org/zh-cn/docs/…
回到今天的话题,今天是想简要讲解一下 Dubbo 的源码,对 Dubbo 的各个模块源码及原理解析,目前 Dubbo 已经交给 Apache 基金会进行孵化,挂在了 GitHub 开源。
- Dubbo 的源码地址 github.com/apache/dubb…
Dubbo 项目结构介绍
- 下面就是把源码 clone 下来后的结构
dubbo 是基于 Maven 构建的,整个项目被拆分成很多的小 Maven 项目,我们一个一个项目慢慢解释。
在 dubbo 的官网上有这样的一副图,为我们指明了 dubbo 框架的模块分包之间的关系。
模块说明(以下均是官网原文,为方便阅读粘贴过来)
- dubbo-common 公共逻辑模块:包括 Util 类和通用模型。
- dubbo-remoting 远程通讯模块:相当于 Dubbo 协议的实现,如果 RPC 用 RMI协议则不需要使用此包。
- dubbo-rpc 远程调用模块:抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。
- dubbo-cluster 集群模块:将多个服务提供方伪装为一个提供方,包括:负载均衡, 容错,路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。
- dubbo-registry 注册中心模块:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
- dubbo-monitor 监控模块:统计服务调用次数,调用时间的,调用链跟踪的服务。
- dubbo-config 配置模块:是 Dubbo 对外的 API,用户通过 Config 使用Dubbo,隐藏 Dubbo 所有细节。
- dubbo-container 容器模块:是一个 Standlone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。
从以上的描述中我们可以比较清晰的了解 dubbo 各个关键模块的依赖关系。下面会对各个模块做简单的介绍,让大家至少清楚各个模块的作用。
dubbo-registry——注册中心模块
先看 dubbo-registry 的项目结构
可以看到 dubbo 的注册中心实现有非常多种。目前较为常用的就是 zookeeper 注册中心了。这个模块封装的就是 dubbo 所支持的注册中心实现。
dubbo-registry-api 中抽象了注册中心的注册和发现,同时实现了一些公共方法使得子类只需关注所需的部分方法。(主要的扩展接口有:Registry,RegistryService, RegistryFactory)
-
Registry 主要提供了重新注册,重新注销接口
-
RegistryService 主要提供了服务的注册,注销,订阅,不订阅等接口
-
RegistryFactory 主要提供了获取 Registry 的接口
dubbo-cluster——集群模块
官网给出了集群模块的工作时序图
可以看到集群模块将多种集群容错时的策略方案整合并只提供出了一个 Invoker 接口供服务消费者使用。
集群模块中包含有以下几种组件:
- Cluster 在服务消费者初始化期间,Cluster 实现类为服务消费者创建 Cluster Invoker 实例。及上图的 merge 操作。
- Cluster Invoker 在服务消费者进行远程调用时,Cluster Invoker 会首先调用 Directory 的 list 方法来获取 Invoker 列表,获取 Invoker 列表通过 LoadBalance 从 Invoker 列表中选择一个 Invoker 。最后将参数传给选择出的 Invoker 实例的 invoke 方法进行方法调用
- Directory 保存 Invoker 列表,调用 Router 的 route 方法进行路由,过滤掉不符合规则的 Invoker。
- Invoker 是服务端某个调用 Service 抽象,里面有一个 invoke 方法,传入了 Invocation对象,这个对象封装了服务地址,服务名,方法名,方法参数列表
- Router 提供路由规则
- LoadBalance 提供负载规则供 Cluster Invoker 选出一个可用的 Invoker
Dubbo 主要提供了以下几种集群容错方式
- Failover Cluster - 失败自动切换
- Failfast Cluster - 快速失败
- Failsafe Cluster - 失败安全
- Failback Cluster - 失败自动恢复
- Forking Cluster - 并行调用多个服务提供者
集群模块的项目结构图
- configurator 包:dubbo 采用 URL 来作为配置信息的格式,所有外部补充点都是通过传递 URL 来携带所需的配置信息,这个包就是用来生成配置信息(总感觉跟 ES 很像哈哈哈哈)
- directory 包:Directory 类的实现类上文也有说,简单来说 Directory 维护了 Invoker 列表,并会跟随着注册中心的服务变更产生变化。
- governance包:集群治理规则包,提供了
GovernanceRuleRepository接口用来添加外部监听类以实现集群治理功能(2019年新增包和类) - interceptor包:Cluster Invoker 拦截器包,用于在 Cluster Invoker 之前和之后添加处理逻辑。
- loadbalance包:封装了负载均衡的实现,Cluster Invoker 在从 directory 中拿到 Invoker 列表后会利用负载均衡实现类从中选出具体的一个 Invoker 用于本次的调用,如果调用失败,则重新选择。
- merger包:封装了返回结果的合并具体方式。支持多种数据类型。
- router包:封装了 Directory 实现类在获取 Invoker 列表时的路由规则 route 方法具体实现。路由规则决定了一次 dubbo 服务调用的目标服务器。
- support包:封装了各类 Cluster Invoker 和 Cluster,包括集群容错模式和分组聚合的 Cluster 以及相关的 Cluster Invoker。
dubbo-common——公共逻辑模块
duubo-common 项目结构
主要封装了一些 dubbo 项目中常用的工具类及统一的对象模型,如在整个项目中都用到的 URL 类.。
dubbo-config——dubbo 配置模块
提供了用户使用配置来使用 dubbo 的四种配置方式,包括XML配置、属性配置、API配置、注解配置。
dubbo-config 项目目录
dubbo-config-api 项目主要实现了 API 配置和属性配置的功能。
dubbo-config-spring 项目主要实现了基于 XML 配置和注解配置的功能。
dubbo-rpc—— dubbo远程调用模块
dubbo 对远程调用协议的具体实现,dubbo 提供了很多种的远程调用协议,但官方推荐使用 dubbo 自己的协议。
dubbo-rpc 项目目录
dubbo-rpc-api 抽象了动态代理和各类协议,上文的 Invoker,Invocation 就是在这个包中定义的
另外的包都是各类协议的实现,下面主要讲解 dubbo-rpc-dubbo 包的内容
dubbo-rpc-dubbo 项目结构
在讲解之前,我们先了解下什么是 dubbo 协议
dubbo 协议是 dubbo 默认使用的一种协议,适合于数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。
但 dubbo 协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
DubboProtocol 定义了 dubbo 协议的具体实现,包括默认端口 port 和 协议名。实现了对于服务的暴露和服务调用过程。在 ServiceBean 初始化时发布服务
会调用 DubboProtocol 的 export 方法进行服务暴露,而在引用服务时也会调用 DubboProtocol 的 protocolBindingRefer 方法进行服务的调用。
DubboInvoker 是 dubbo 协议在服务调用过程中的 Invoker 实现。在服务消费时,会通过 directory,router,loadbalance 选择一个 Invoker 调用。
DubboExporter 定义了 dubbo 协议的服务暴露具体实现。
DubboCodec 封装了在服务调用过程中编解码逻辑。
DecodeableRpcInvocation 和 DecodeableRpcResult 是服务调用时 Invocation 和 result 的具体实现类。在 DubboCodec 返回结果会用到这两个类。
dubbo-remoting——远程通信模块
在 dubbo 框架中,官方为我们提供了很多种客户端和服务端通信的功能,比如 Netty, Tomcat 等等,这个包主要是对 dubbo 协议的具体实现(如果使用的时 RMI 协议则不需要是用这个包)
dubbo-remoting 项目目录
dubbo-remoting-api:定义了客户端和服务端所需要使用的接口
dubbo-remoting-etcd3:定义了使用 etcd3 作为具体实现的 Client 和 Server
dubbo-remoting-grizzly:定义了使用 Grizzly 实现的Client和Server。
dubbo-remoting-http:定义了使用 Jetty 或 Tomcat 或 Servlet 实现的Client和Server。
dubbo-remoting-mina:定义了使用 Mina 实现的 Client 和 Server。
dubbo-remoting-netty / dubbo-remoting-netty4:定义了使用 Netty3 / Netty4 实现的 Client 和 Server。
dubbo-remoting-p2p:P2P服务器,注册中心 multicast 中会用到这个服务器使用。
dubbo-remoting-zookeeper:封装了 Zookeeper Client ,和 Zookeeper Server 通信。在使用 zookeeper 作为注册中心时会使用这个包的内容。
dubbo-container——容器模块
在 dubbo 框架中,后台服务不需要 Tomcat / JBoss 等臃肿的 web 容器的功能,也不需要这些容器去加载服务提供方的服务。为避免资源浪费和减少复杂度,dubbo 框架将服务容器只提供一个简单的 main 方法,用来加载一些内置的容器,同时也支持扩展容器。
dubbo-container 项目目录
dubbo-container-api 定义了 Container 接口,实现了容器加载的 Main 方法
dubbo-container-log4j 实现了 Log4J 的容器实现。
dubbo-container-logback 实现了 logback 的容器实现。
dubbo-container-spring 实现了 spring 的容器实现。
-
扩展容器时只需继承 container 接口重写容器的 start 和 stop 方法即可
-
dubbo-container-api 的 Main 方法
package org.apache.dubbo.container;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
public class Main {
public static final String CONTAINER_KEY = "dubbo.container";
public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";
private static final Logger logger = LoggerFactory.getLogger(Main.class);
// 默认 dubbo 会使用 Spring 的 SpringContainer
private static final ExtensionLoader<Container> LOADER = ExtensionLoader.getExtensionLoader(Container.class);
private static final ReentrantLock LOCK = new ReentrantLock();
private static final Condition STOP = LOCK.newCondition();
public static void main(String[] args) {
try {
if (ArrayUtils.isEmpty(args)) {
String config = ConfigUtils.getProperty(CONTAINER_KEY, LOADER.getDefaultExtensionName());
args = COMMA_SPLIT_PATTERN.split(config);
}
final List<Container> containers = new ArrayList<Container>();
// 将获取到的 container 加入列表,如没有配置则默认加入 SpringContainer
for (int i = 0; i < args.length; i++) {
containers.add(LOADER.getExtension(args[i]));
}
logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
Runtime.getRuntime().addShutdownHook(new Thread("dubbo-container-shutdown-hook") {
@Override
public void run() {
for (Container container : containers) {
try {
container.stop();
logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
try {
LOCK.lock();
STOP.signal();
} finally {
LOCK.unlock();
}
}
}
});
}
// 遍历执行容器
for (Container container : containers) {
container.start();
logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
}
System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
} catch (RuntimeException e) {
logger.error(e.getMessage(), e);
System.exit(1);
}
try {
LOCK.lock();
STOP.await();
} catch (InterruptedException e) {
logger.warn("Dubbo service server stopped, interrupted by other thread!", e);
} finally {
LOCK.unlock();
}
}
}
dubbo-monitor——监控模块
我们都知道 dubbo 框架有一个监控中心用来监控服务的健康情况等信息,这个包就是用来定义一些监控中心所需的相关接口,以及一些过滤器的实现。
dubbo-monitor 项目目录
dubbo-monitor-api:定义了monitor相关的接口,实现了监控所需要的过滤器。
dubbo-monitor-default:实现了dubbo监控相关的功能。
至此,dubbo 框架的主要模块都介绍完毕了,接下来的是 dubbo 框架中一些额外的模块展示。
dubbo-configcenter——配置中心模块
dubbo-configcenter 实现了使用 dubbo 框架的统一配置中心实现,用过 Apollo 的同学都知道,配置中心可以很方便的管理微服务的配置文件,这个模块的引入也表明了 dubbo 正在努力的成为一个微服务解决方案而不仅仅只是一个 RPC 协议框架。
dubbo-configcenter 项目结构
可以看到 dubbo 支持绝大多数的主流配置中心接入,如 Apollo,nacos,zookeeper 等。
dubbo-filter——过滤器模块
filter 模块提供了一些内置的过滤器
dubbo-filter 项目目录
dubbo-filter-cache:提供缓存过滤器。
dubbo-filter-validation:提供参数验证过滤器。
dubbo-plugin——插件模块
plugin 提供了一些内置的插件服务
dubbo-plugin 项目目录
dubbo-auth 提供了认证的相关服务
dubbo-qos 提供了在线运维的命令服务
dubbo-serialization——序列化模块
serialization 模块封装了各类序列化框架的支持实现。
dubbo-serialization 项目目录
dubbo-serialization-api:定义了Serialization的接口以及数据输入输出的接口。
其他的包都是实现了对应的序列化框架的方法。dubbo内置的就是这几类的序列化框架,序列化也支持扩展。
dubbo-metadata——元数据中心
在介绍这个模块之前我们先了解下什么是元数据
元数据定义为描述数据的数据,在服务治理中,例如服务接口名,重试次数,版本号等等都可以理解为元数据。
在 dubbo 2.7 之前,dubbo 框架将服务生产者注册时产生的 30+ 参数和消费者注册时产生的 20+ 参数全部丢在了注册中心中,造成了一系列问题:
推送量大 -> 存储数据量大 -> 网络传输量大 -> 延迟严重
- 在 dubbo 官网的元数据中心参考手册中也介绍了元数据中心的重要性
dubbo-metadata 项目目录
dubbo-metadata-api:抽象了 MetadataReport 和 MetadataReportFactory 接口用来给不同方式的储存方式实现。
其余的都是不同储存方式的实现。
总结
这次简单介绍了下 dubbo 项目中的各个模块,不得不说 dubbo 真的是一套很适合初学者学习的框架,不管是资料还是官网都对 dubbo 有着很详细的解释和说明。
如果我在哪一部分写的不够到位或者写错了,还请在座的各位大佬提出意见。