Skip to content

Commit

Permalink
fix: 更新文档格式
Browse files Browse the repository at this point in the history
  • Loading branch information
yanglbme committed Sep 3, 2020
1 parent 53c9226 commit 580e9c7
Show file tree
Hide file tree
Showing 108 changed files with 4,271 additions and 5,103 deletions.
38 changes: 23 additions & 15 deletions docs/Dubbo/SPI/Dubbo与Java的SPI机制.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
## JDK的SPI思想
## JDK 的 SPI 思想

SPI,即 Service Provider Interface。在面向对象的设计里面,模块之间推荐基于接口编程,而不是对实现类进行硬编码,这样做也是为了模块设计的可插拔原则。

比较典型的应用,如 JDBC,Java 定义了一套 JDBC 的接口,但是 Java 本身并不提供对 JDBC 的实现类,而是开发者根据项目实际使用的数据库来选择驱动程序 jar包,比如 mysql,你就将 mysql-jdbc-connector.jar 引入进来;oracle,你就将 oracle-jdbc-connector.jar 引入进来。在系统跑的时候,碰到你使用 jdbc 的接口,他会在底层使用你引入的那个 jar 中提供的实现类。
比较典型的应用,如 JDBC,Java 定义了一套 JDBC 的接口,但是 Java 本身并不提供对 JDBC 的实现类,而是开发者根据项目实际使用的数据库来选择驱动程序 jar 包,比如 mysql,你就将 mysql-jdbc-connector.jar 引入进来;oracle,你就将 oracle-jdbc-connector.jar 引入进来。在系统跑的时候,碰到你使用 jdbc 的接口,他会在底层使用你引入的那个 jar 中提供的实现类。

## Dubbo 的 SPI 扩展机制原理

dubbo 自己实现了一套 SPI 机制,并对 JDK 的 SPI 进行了改进。

## Dubbo的SPI扩展机制原理
dubbo自己实现了一套SPI机制,并对 JDK的SPI进行了改进。
1. JDK标准的SPI只能通过遍历来查找扩展点和实例化,有可能导致一次性加载所有的扩展点,如果不是所有的扩展点都被用到,就会导致资源的浪费。dubbo每个扩展点都有多种实现,例如:com.alibaba.dubbo.rpc.Protocol接口有InjvmProtocol、DubboProtocol、RmiProtocol、HttpProtocol、HessianProtocol等实现,如果只是用到其中一个实现,可是加载了全部的实现,会导致资源的浪费。
2. 对配置文件中扩展实现的格式的修改,例如,META-INF/dubbo/com.xxx.Protocol 里的 com.foo.XxxProtocol格式 改为了 xxx = com.foo.XxxProtocol 这种以键值对的形式,这样做的目的是为了让我们更容易的定位到问题。比如,由于第三方库不存在,无法初始化,导致无法加载扩展点(“A”),当用户配置使用A时,dubbo就会报无法加载扩展点的错误,而不是报哪些扩展点的实现加载失败以及错误原因,**这是因为原来的配置格式没有记录扩展名的id,导致dubbo无法抛出较为精准的异常,这会加大排查问题的难度**。所以改成key-value的形式来进行配置。
3. dubbo的SPI机制增加了对IOC、AOP的支持,一个扩展点可以直接通过setter注入到其他扩展点。
1. JDK 标准的 SPI 只能通过遍历来查找扩展点和实例化,有可能导致一次性加载所有的扩展点,如果不是所有的扩展点都被用到,就会导致资源的浪费。dubbo 每个扩展点都有多种实现,例如:com.alibaba.dubbo.rpc.Protocol 接口有 InjvmProtocol、DubboProtocol、RmiProtocol、HttpProtocol、HessianProtocol 等实现,如果只是用到其中一个实现,可是加载了全部的实现,会导致资源的浪费。
2. 对配置文件中扩展实现的格式的修改,例如,META-INF/dubbo/com.xxx.Protocol 里的 com.foo.XxxProtocol 格式 改为了 xxx = com.foo.XxxProtocol 这种以键值对的形式,这样做的目的是为了让我们更容易的定位到问题。比如,由于第三方库不存在,无法初始化,导致无法加载扩展点(“A”),当用户配置使用 A 时,dubbo 就会报无法加载扩展点的错误,而不是报哪些扩展点的实现加载失败以及错误原因,**这是因为原来的配置格式没有记录扩展名的 id,导致 dubbo 无法抛出较为精准的异常,这会加大排查问题的难度**。所以改成 key-value 的形式来进行配置。
3. dubbo 的 SPI 机制增加了对 IOC、AOP 的支持,一个扩展点可以直接通过 setter 注入到其他扩展点。

下面我们看一下Dubbo 的 SPI扩展机制实现的结构目录
下面我们看一下 Dubbo 的 SPI 扩展机制实现的结构目录

![avatar](../../../images/Dubbo/SPI组件目录结构.png)

### SPI 注解
首先看一下 SPI注解。在某个接口上加上 @SPI 注解后,表明该接口为可扩展接口。比如,协议扩展接口Protocol,如果使用者在 <dubbo:protocol />、<dubbo:service />、<dubbo:reference /> 都没有指定 protocol属性 的话,那么就默认使用 DubboProtocol 作为接口Protocol的实现,因为在 Protocol 上有 @SPI("dubbo")注解。而这个 protocol属性值 或者默认值会被当作该接口的实现类中的一个key,dubbo 会去 META-INF.dubbo.internal下的com.alibaba.dubbo.rpc.Protocol文件中找该key对应的value,源码如下。

首先看一下 SPI 注解。在某个接口上加上 @SPI 注解后,表明该接口为可扩展接口。比如,协议扩展接口 Protocol,如果使用者在 <dubbo:protocol />、<dubbo:service />、<dubbo:reference /> 都没有指定 protocol 属性 的话,那么就默认使用 DubboProtocol 作为接口 Protocol 的实现,因为在 Protocol 上有 @SPI("dubbo")注解。而这个 protocol 属性值 或者默认值会被当作该接口的实现类中的一个 key,dubbo 会去 META-INF.dubbo.internal 下的 com.alibaba.dubbo.rpc.Protocol 文件中找该 key 对应的 value,源码如下。

```java
/**
* 协议接口
Expand All @@ -41,7 +46,7 @@ public interface Protocol {
*/
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

/**
* 引用远程服务:<br>
* 1. 当用户调用 refer() 所返回的 Invoker 对象的 invoke() 方法时,协议需相应执行同 URL 远端 export() 传入的 Invoker 对象的 invoke() 方法。<br>
Expand All @@ -56,7 +61,7 @@ public interface Protocol {
*/
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

/**
* 释放协议:<br>
* 1. 取消该协议所有已经暴露和引用的服务。<br>
Expand Down Expand Up @@ -107,10 +112,13 @@ public @interface SPI {
// 配置文件 com.alibaba.dubbo.rpc.Protocol 中的内容
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
```
value 就是该 Protocol接口 的实现类 DubboProtocol,这样就做到了SPI扩展。

value 就是该 Protocol 接口 的实现类 DubboProtocol,这样就做到了 SPI 扩展。

### ExtensionLoader
ExtensionLoader 扩展加载器,这是 dubbo 实现 SPI扩展机制 的核心,几乎所有实现的逻辑都被封装在 ExtensionLoader 中,其源码如下。

ExtensionLoader 扩展加载器,这是 dubbo 实现 SPI 扩展机制 的核心,几乎所有实现的逻辑都被封装在 ExtensionLoader 中,其源码如下。

```java
/**
* 拓展加载器,Dubbo使用的扩展点获取
Expand Down Expand Up @@ -496,7 +504,7 @@ public class ExtensionLoader<T> {
getExtensionClasses();
// 如果为 true ,不能继续调用 `#getExtension(true)` 方法,会形成死循环。
if (null == cachedDefaultName || cachedDefaultName.length() == 0
|| "true".equals(cachedDefaultName)) {
|| "true".equals(cachedDefaultName)) {
return null;
}
return getExtension(cachedDefaultName);
Expand Down Expand Up @@ -1237,4 +1245,4 @@ public class ExtensionLoader<T> {
return this.getClass().getName() + "[" + type.getName() + "]";
}
}
```
```
37 changes: 26 additions & 11 deletions docs/Dubbo/architectureDesign/Dubbo整体架构.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,51 @@
## 项目结构
首先从GitHub 上 clone下来 Dubbo项目,我们根据其中各子项目的项目名,也能大概猜出来各个模块的作用。

首先从 GitHub 上 clone 下来 Dubbo 项目,我们根据其中各子项目的项目名,也能大概猜出来各个模块的作用。

![avatar](../../../images/Dubbo/dubbo项目结构.png)

### dubbo-common

公共逻辑子项目,定义了各子项目中 通用的 组件 和 工具类,如:IO、日志、配置处理等。

### dubbo-rpc
分布式协调服务框架的核心,该模块定义了 RPC相关的组件,包括 服务发布、服务调用代理、远程调用结果、RPC调用网络协议,RPC调用监听器和过滤器等等。该模块提供了默认的 基于dubbo协议的实现,还提供了hessian、http、rmi、及webservice等协议的实现,能够满足绝大多数项目的使用需求,另外 还提供了对自定义协议的扩展。

分布式协调服务框架的核心,该模块定义了 RPC 相关的组件,包括 服务发布、服务调用代理、远程调用结果、RPC 调用网络协议,RPC 调用监听器和过滤器等等。该模块提供了默认的 基于 dubbo 协议的实现,还提供了 hessian、http、rmi、及 webservice 等协议的实现,能够满足绝大多数项目的使用需求,另外 还提供了对自定义协议的扩展。

### dubbo-registry
注册中心子项目,它是 RPC 中 consumer服务消费者 和 provider服务提供者 两个重要角色的协调者,该子项目定义了核心的 注册中心组件,提供了 mutilcast、redis 和 zookeeper 等多种方式的注册中心实现,用于不同的使用场景。当然,几乎所有的项目都会选择基于zookeeper的实现。

注册中心子项目,它是 RPC 中 consumer 服务消费者 和 provider 服务提供者 两个重要角色的协调者,该子项目定义了核心的 注册中心组件,提供了 mutilcast、redis 和 zookeeper 等多种方式的注册中心实现,用于不同的使用场景。当然,几乎所有的项目都会选择基于 zookeeper 的实现。

### dubbo-remoting
远程通讯子项目,RPC 的实现基础就是远程通讯,consmer 要调用 provider 的远程方法必须通过 远程通讯实现。该模块定义了远程传输器、endpoint 终端、客户端、服务端、编码解码器、数据交换、缓冲区、通讯异常定义 等核心组件。他是对于远程网络通讯的抽象,提供了诸如 netty、mina、http等 协议和技术框架的实现方式。

远程通讯子项目,RPC 的实现基础就是远程通讯,consmer 要调用 provider 的远程方法必须通过 远程通讯实现。该模块定义了远程传输器、endpoint 终端、客户端、服务端、编码解码器、数据交换、缓冲区、通讯异常定义 等核心组件。他是对于远程网络通讯的抽象,提供了诸如 netty、mina、http 等 协议和技术框架的实现方式。

### dubbo-monitor
监控子项目,该模块可以监控服务调用的各种信息,例如调用耗时、调用量、调用结果等等,监控中心在调用过程中收集调用的信息,发送到监控服务,在监控服务中可以存储这些信息,对这些数据进行统计分析 和 展示。dubbo默认提供了一个实现,该实现非常简单,只是作为默认的实现范例,生产环境使用价值不高,往往需要自行实现。

监控子项目,该模块可以监控服务调用的各种信息,例如调用耗时、调用量、调用结果等等,监控中心在调用过程中收集调用的信息,发送到监控服务,在监控服务中可以存储这些信息,对这些数据进行统计分析 和 展示。dubbo 默认提供了一个实现,该实现非常简单,只是作为默认的实现范例,生产环境使用价值不高,往往需要自行实现。

### dubbo-container
容器子项目,是一个独立的容器,以简单的 Main(类) 加载Spring启动,因为服务通常不需要Tomcat/JBoss等Web容器的特性,没必要用Web容器去加载服务。

容器子项目,是一个独立的容器,以简单的 Main(类) 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。

### dubbo-config
配置中心子项目,该模块通过 配置信息,将dubbo组件的各个模块整合在一起,给 框架的使用者 提供 可配置的、易用的 分布式服务框架。它定义了面向dubbo使用者的各种信息配置,比如服务发布配置、方法发布配置、服务消费配置、应用程序配置、注册中心配置、协议配置、监控配置等等。

### dubbo-cluster
配置中心子项目,该模块通过 配置信息,将 dubbo 组件的各个模块整合在一起,给 框架的使用者 提供 可配置的、易用的 分布式服务框架。它定义了面向 dubbo 使用者的各种信息配置,比如服务发布配置、方法发布配置、服务消费配置、应用程序配置、注册中心配置、协议配置、监控配置等等。

### dubbo-cluster

集群子项目,将多个服务提供方伪装为一个提供方,包括:负载均衡、容错、路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。

### dubbo-admin
该子项目是一个web应用,可以独立部署,用于管理 dubbo服务,该管理应用可以连接注册中心,读取和更新 注册中心中的内容。

该子项目是一个 web 应用,可以独立部署,用于管理 dubbo 服务,该管理应用可以连接注册中心,读取和更新 注册中心中的内容。

## 实现原理

### 角色类型与运行原理
一个Dubbo项目 的角色主要分为如下五种。

一个 Dubbo 项目 的角色主要分为如下五种。

- Provider:服务提供方;
- Consumer:服务消费方;
- Registry:服务注册与发现的注册中心;
Expand All @@ -44,10 +57,12 @@
![avatar](../../../images/Dubbo/Dubbo工作原理图.png)

### 工作原理

最后总结下其工作原理。

1. 服务导出:服务提供方 导出服务,监听服务端口;
2. 服务注册:服务提供方 注册服务信息到注册中心;
3. 服务订阅:服务消费方 订阅关注的服务;
4. 服务发现:当服务地址发生变更时,注册中心通知服务消费端;
5. 远程服务调用 :根据负载均衡策略 选择服务地址,直接调用;
6. 监控:监控器 收集和展示 服务提供方、服务消费方之间 的服务调用统计信息 。
6. 监控:监控器 收集和展示 服务提供方、服务消费方之间 的服务调用统计信息 。
24 changes: 14 additions & 10 deletions docs/Dubbo/cluster/Dubbo集群模块简析.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
### 集群模块简介
集群,是指同一个服务 被部署在了多个服务器上,每个服务器的任务都相同,能够以较高的性价比,提升系统的 性能、可靠性、灵活性,但同时也要面对 集群中会出现的 负载均衡、容错等问题。dubbo的集群模块,主要涉及以下几部分内容。
- 负载均衡策略:dubbo支持的所有负载均衡策略算法;

集群,是指同一个服务 被部署在了多个服务器上,每个服务器的任务都相同,能够以较高的性价比,提升系统的 性能、可靠性、灵活性,但同时也要面对 集群中会出现的 负载均衡、容错等问题。dubbo 的集群模块,主要涉及以下几部分内容。

- 负载均衡策略:dubbo 支持的所有负载均衡策略算法;
- 集群容错:Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个;
- 路由:dubbo路由规则,路由规则决定了一次dubbo服务调用的目标服务器,路由规则分两种:条件路由规则和脚本路由规则,并且支持可拓展;
- 配置:根据url上的配置规则生成配置信息
- 路由:dubbo 路由规则,路由规则决定了一次 dubbo 服务调用的目标服务器,路由规则分两种:条件路由规则和脚本路由规则,并且支持可拓展;
- 配置:根据 url 上的配置规则生成配置信息
- 分组聚合:合并返回结果;
- 本地伪装:mock通常用于服务降级,mock只在非业务异常时执行,如 超时、网络异常等。
- 本地伪装:mock 通常用于服务降级,mock 只在非业务异常时执行,如 超时、网络异常等。

集群工作过程可分为两个阶段,第一个阶段是在消费者初始化期间,集群 Cluster 为消费者创建 ClusterInvoker 实例。第二个阶段是在消费者进行RPC时,以 FailoverClusterInvoker 为例,该实例首先会调用 Directory 的 list()方法 获取 Invoker列表,然后根据配置的 负载均衡策略,从 Invoker列表 中选择一个 Inovker,最后将参数传给选择出的 Invoker实例 进行真正的远程调用。
集群工作过程可分为两个阶段,第一个阶段是在消费者初始化期间,集群 Cluster 为消费者创建 ClusterInvoker 实例。第二个阶段是在消费者进行 RPC 时,以 FailoverClusterInvoker 为例,该实例首先会调用 Directory 的 list()方法 获取 Invoker 列表,然后根据配置的 负载均衡策略,从 Invoker 列表 中选择一个 Inovker,最后将参数传给选择出的 Invoker 实例 进行真正的远程调用。

可将上文中出现的 Invoker 简单理解为服务提供者,Directory 的用途是保存 Invoker列表,实现类 RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Inovker 列表会随着注册中心内容的变化而变化。每次变化后,RegistryDirectory 会动态增删 Inovker,并调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker。
可将上文中出现的 Invoker 简单理解为服务提供者,Directory 的用途是保存 Invoker 列表,实现类 RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Inovker 列表会随着注册中心内容的变化而变化。每次变化后,RegistryDirectory 会动态增删 Inovker,并调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker。

下面我们来看一下 集群模块的项目结构图,结合上文的描述,可以对其有更加深刻的理解。

![avatar](../../../images/Dubbo/dubbo-cluster模块工程结构.png)

### 集群模块核心API 源码解析
从上图应该也能看出其核心API在哪个包里。
### 集群模块核心 API 源码解析

从上图应该也能看出其核心 API 在哪个包里。

![avatar](../../../images/Dubbo/com.alibaba.dubbo.rpc.cluster包目录.png)

各核心接口的源码如下。

```java
/**
* 集群接口
Expand Down Expand Up @@ -135,4 +139,4 @@ public interface RouterFactory {
@Adaptive("protocol")
Router getRouter(URL url);
}
```
```
Loading

0 comments on commit 580e9c7

Please sign in to comment.