diff --git a/docs/Dubbo/cluster/Cluster组件.md b/docs/Dubbo/cluster/mock与服务降级.md
similarity index 100%
rename from docs/Dubbo/cluster/Cluster组件.md
rename to docs/Dubbo/cluster/mock与服务降级.md
diff --git a/docs/Dubbo/cluster/集群基础.md b/docs/Dubbo/cluster/集群容错.md
similarity index 100%
rename from docs/Dubbo/cluster/集群基础.md
rename to docs/Dubbo/cluster/集群容错.md
diff --git a/docs/Dubbo/registry/Dubbo注册中心.md b/docs/Dubbo/registry/Dubbo注册中心.md
deleted file mode 100644
index 2e94ce0..0000000
--- a/docs/Dubbo/registry/Dubbo注册中心.md
+++ /dev/null
@@ -1,2171 +0,0 @@
-## 注册中心在Dubbo中的作用
-服务治理框架可以大致分为 服务通信 和 服务管理 两部分,服务管理可以分为服务注册、服务订阅以及服务发现,服务提供者Provider 会往注册中心注册服务,而消费者Consumer 会从注册中心中订阅自己关注的服务,并在关注的服务发生变更时 得到注册中心的通知。Provider、Consumer以及Registry之间的依赖关系 如下图所示。
-
-![avatar](/images/Dubbo/Dubbo原理图.png)
-
-## dubbo-registry 模块 结构分析
-dubbo的注册中心有多种实现方案,如:zookeeper、redis、multicast等,本章先看一下 dubbo-registry 模块的核心部分 dubbo-registry-api,具体实现部分放到下章来讲。dubbo-registry模块 的结构如下图所示。
-
-![avatar](/images/Dubbo/dubbo-registry模块结构图.png)
-
-### Registry 核心组件类图
-典型的 接口 -> 抽象类 -> 实现类 的结构设计,如下图所示。
-
-![avatar](/images/Dubbo/Registry组件类图.png)
-
-既然有Registry组件,那么按照很多框架的套路,肯定也有一个用于获取 Registry实例的RegistryFactory,其中用到了工厂方法模式,不同的工厂类用于获取不同类型的实例。其类图结构如下。
-
-![avatar](/images/Dubbo/RegistryFactory组件类图.png)
-
-## 源码详解
-根据上面的类图,我们开始从上往下 详解dubbo中对于注册中心的设计以及实现。
-### RegistryService 接口
-RegistryService 是注册中心模块的服务接口,定义了注册、取消注册、订阅、取消订阅以及查询符合条件的已注册数据 等方法。这里统一说明一下URL,dubbo是以总线模式来时刻传递和保存配置信息的,配置信息都被放在URL上进行传递,随时可以取得相关配置信息,而这里提到了URL有别的作用,就是作为类似于节点的作用,首先服务提供者(Provider)启动时需要提供服务,就会向注册中心写下自己的URL地址。然后消费者启动时需要去订阅该服务,则会订阅Provider注册的地址,并且消费者也会写下自己的URL。
-```java
-/**
- * RegistryService. (SPI, Prototype, ThreadSafe)
- *
- * 注册中心服务接口
- */
-public interface RegistryService {
-
- /**
- * 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则 等数据。
- *
- * 注册需处理契约:
- * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。
- * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。
- * 3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。
- * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。
- * 5. 允许URI相同但参数不同的URL并存,不能覆盖。
- *
- * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
- */
- void register(URL url);
-
- /**
- * 取消注册.
- *
- * 取消注册需处理契约:
- * 1. 如果是dynamic=false的持久存储数据,找不到注册数据,则抛IllegalStateException,否则忽略。
- * 2. 按全URL匹配取消注册。
- *
- * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
- */
- void unregister(URL url);
-
- /**
- * 订阅符合条件的已注册数据,当有注册数据变更时自动推送.
- *
- * 订阅需处理契约:
- * 1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。
- * 2. 当URL设置了category=routers,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。
- * 3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&version=1.0.0
- * 4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&group=*&version=*&classifier=*
- * 5. 当注册中心重启,网络抖动,需自动恢复订阅请求。
- * 6. 允许URI相同但参数不同的URL并存,不能覆盖。
- * 7. 必须阻塞订阅过程,等第一次通知完后再返回。
- *
- * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
- * @param listener 变更事件监听器,不允许为空
- */
- void subscribe(URL url, NotifyListener listener);
-
- /**
- * 取消订阅.
- *
- * 取消订阅需处理契约:
- * 1. 如果没有订阅,直接忽略。
- * 2. 按全URL匹配取消订阅。
- *
- * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
- * @param listener 变更事件监听器,不允许为空
- */
- void unsubscribe(URL url, NotifyListener listener);
-
- /**
- * 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,只返回一次结果。
- *
- * @param url 查询条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
- * @return 已注册信息列表,可能为空,含义同{@link com.alibaba.dubbo.registry.NotifyListener#notify(List)}的参数。
- * @see com.alibaba.dubbo.registry.NotifyListener#notify(List)
- */
- List lookup(URL url);
-}
-```
-
-### Registry 接口
-注册中心接口,把节点Node 以及注册中心服务RegistryService 的方法整合在了这个接口里面。该接口并没有自己的方法,就是继承了Node和RegistryService接口。这里的Node是节点的接口,里面协定了关于节点的一些操作方法,源码如下。
-```java
-/**
- * 注册中心接口
- */
-public interface Registry extends Node, RegistryService {
-}
-
-public interface Node {
- //获得节点地址
- URL getUrl();
- //判断节点是否可用
- boolean isAvailable();
- //销毁节点
- void destroy();
-}
-```
-
-### AbstractRegistry 抽象类
-实现了Registry接口的抽象类。为了减轻注册中心的压力,该抽象类把本地URL缓存到了property文件中,并且实现了注册中心的注册、订阅等方法。
-```java
-/**
- * 实现了Registry接口的抽象类,实现了如下方法:
- *
- * 1、通用的注册、订阅、查询、通知等方法
- * 2、读取和持久化注册数据到文件,以 properties 格式存储
- */
-public abstract class AbstractRegistry implements Registry {
-
- // URL地址分隔符,用于文件缓存中,服务提供者URL分隔
- private static final char URL_SEPARATOR = ' ';
- // URL地址分隔正则表达式,用于解析文件缓存中服务提供者URL列表
- private static final String URL_SPLIT = "\\s+";
-
- // Log output
- protected final Logger logger = LoggerFactory.getLogger(getClass());
- /**
- * 本地磁盘缓存。
- * 1. 其中特殊的 key 值 .registies 记录注册中心列表 TODO 8019 芋艿,特殊的 key 是
- * 2. 其它均为 {@link #notified} 服务提供者列表
- */
- private final Properties properties = new Properties();
- /**
- * 注册中心缓存写入执行器。
- * 线程数=1
- */
- // File cache timing writing
- private final ExecutorService registryCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveRegistryCache", true));
- /**
- * 是否同步保存文件
- */
- private final boolean syncSaveFile;
- /**
- * 数据版本号
- */
- private final AtomicLong lastCacheChanged = new AtomicLong();
- /**
- * 已注册 URL 集合。
- * 注册的 URL 可以是服务提供者的,也可以是服务消费者的
- */
- private final Set registered = new ConcurrentHashSet();
- /**
- * 订阅 URL 的监听器集合
- * key:订阅者的 URL ,例如消费者的 URL
- */
- private final ConcurrentMap> subscribed = new ConcurrentHashMap>();
- /**
- * 被通知的 URL 集合
- * key1:消费者的 URL ,例如消费者的 URL ,和 {@link #subscribed} 的键一致
- * key2:分类,例如:providers、consumers、routes、configurators。【实际无 consumers ,因为消费者不会去订阅另外的消费者的列表】
- * 在 {@link Constants} 中,以 "_CATEGORY" 结尾
- */
- private final ConcurrentMap>> notified = new ConcurrentHashMap>>();
- /**
- * 注册中心 URL
- */
- private URL registryUrl;
- /**
- * 本地磁盘缓存文件,缓存注册中心的数据
- */
- private File file;
- /**
- * 是否销毁
- */
- private AtomicBoolean destroyed = new AtomicBoolean(false);
-
- public AbstractRegistry(URL url) {
- setUrl(url);
- // Start file save timer
- syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
- // 获得 `file`
- String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache");
- File file = null;
- if (ConfigUtils.isNotEmpty(filename)) {
- file = new File(filename);
- if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
- if (!file.getParentFile().mkdirs()) {
- throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
- }
- }
- }
- this.file = file;
- // 加载本地磁盘缓存文件到内存缓存
- loadProperties();
- // 通知监听器,URL 变化结果
- notify(url.getBackupUrls()); // 【TODO 8020】为什么构造方法,要通知,连监听器都没注册
- }
-
- protected static List filterEmpty(URL url, List urls) {
- if (urls == null || urls.isEmpty()) {
- List result = new ArrayList(1);
- result.add(url.setProtocol(Constants.EMPTY_PROTOCOL));
- return result;
- }
- return urls;
- }
-
- @Override
- public URL getUrl() {
- return registryUrl;
- }
-
- protected void setUrl(URL url) {
- if (url == null) {
- throw new IllegalArgumentException("registry url == null");
- }
- this.registryUrl = url;
- }
-
- public Set getRegistered() {
- return registered;
- }
-
- public Map> getSubscribed() {
- return subscribed;
- }
-
- public Map>> getNotified() {
- return notified;
- }
-
- public File getCacheFile() {
- return file;
- }
-
- public Properties getCacheProperties() {
- return properties;
- }
-
- public AtomicLong getLastCacheChanged() {
- return lastCacheChanged;
- }
-
- /**
- * 保存内存缓存到本地磁盘缓存文件,即 {@link #properties} => {@link #file}
- *
- * @param version 数据版本号
- */
- public void doSaveProperties(long version) {
- if (version < lastCacheChanged.get()) {
- return;
- }
- if (file == null) {
- return;
- }
- // Save
- try {
- // 创建 .lock 文件
- File lockfile = new File(file.getAbsolutePath() + ".lock");
- if (!lockfile.exists()) {
- lockfile.createNewFile();
- }
- // 随机读写文件操作
- RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
- try {
- FileChannel channel = raf.getChannel();
- try {
- // 获得文件锁
- FileLock lock = channel.tryLock();
- // 获取失败
- if (lock == null) {
- throw new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
- }
- // 获取成功,进行保存
- // Save
- try {
- if (!file.exists()) {
- file.createNewFile();
- }
- FileOutputStream outputFile = new FileOutputStream(file);
- try {
- properties.store(outputFile, "Dubbo Registry Cache");
- } finally {
- outputFile.close();
- }
- // 释放文件锁
- } finally {
- lock.release();
- }
- // 释放文件 Channel
- } finally {
- channel.close();
- }
- // 释放随机读写文件操作
- } finally {
- raf.close();
- }
- } catch (Throwable e) {
- // 版本号过小,不保存
- if (version < lastCacheChanged.get()) {
- return;
- // 重新异步保存,一般情况下为上面的获取锁失败抛出的异常。通过这样的方式,达到保存成功。
- } else {
- registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
- }
- logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
- }
- }
-
- /**
- * 加载本地磁盘缓存文件到内存缓存,即 {@link #file} => {@link #properties}
- */
- private void loadProperties() {
- if (file != null && file.exists()) {
- InputStream in = null;
- try {
- // 文件流
- in = new FileInputStream(file);
- // 读取文件流
- properties.load(in);
- if (logger.isInfoEnabled()) {
- logger.info("Load registry store file " + file + ", data: " + properties);
- }
- } catch (Throwable e) {
- logger.warn("Failed to load registry store file " + file, e);
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- logger.warn(e.getMessage(), e);
- }
- }
- }
- }
- }
-
- /**
- * 从 `properties` 中获得缓存的 URL 集合
- *
- * @param url URL
- * @return URL 集合
- */
- public List getCacheUrls(URL url) {
- for (Map.Entry