This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 1e8c046fa0d Submit source blog (#1380)
1e8c046fa0d is described below

commit 1e8c046fa0d44f533a9ebd89073f9cc00dae4d95
Author: songxiaosheng <[email protected]>
AuthorDate: Sun Aug 14 14:45:27 2022 +0800

    Submit source blog (#1380)
    
    * add dubbo3.0.8 1-demo blog
    
    * add dubbo3.0.8 1-demo blog
    
    * add authro
    
    * add author
    
    * remove author path
    
    * add ServiceConfig
    
    * Dubbo3.0.8源码解析ServiceConfig对象的建模
    
    * Dubbo3.0.8源码解析ServiceConfig对象的建模
    
    * Dubbo3.0.8源码解析ServiceConfig对象的建模
    
    * Dubbo3.0.8源码解析ServiceConfig对象的建模
    
    * 框架,应用程序,模块领域模型Model对象的初始化博客
    
    * Dubbo扩展机制
    
    * Dubbo扩展机制
    
    * dubbo启动器源码解析
    
    * dubbo启动器源码解析
    
    * Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig
    
    * Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig
    
    * Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig
    
    * Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig
    
    * remove blank
    
    * remove blank
---
 ...5\256\344\277\241\346\201\257RegistryConfig.md" | 229 +++++++++++
 ...5\256\344\277\241\346\201\257ProtocolConfig.md" | 109 ++++++
 ...224\237\345\221\275\345\221\250\346\234\237.md" | 349 +++++++++++++++++
 ...205\215\347\275\256\344\270\255\345\277\203.md" | 430 +++++++++++++++++++++
 ...232\204\345\210\235\345\247\213\345\214\226.md" | 120 ++++++
 ...56\344\277\241\346\201\257ApplicationConfig.md" | 194 ++++++++++
 static/imgs/blog/source-blog/13-config-1.png       | Bin 0 -> 130882 bytes
 static/imgs/blog/source-blog/13-config2.png        | Bin 0 -> 104484 bytes
 static/imgs/blog/source-blog/register.png          | Bin 0 -> 814937 bytes
 9 files changed, 1431 insertions(+)

diff --git 
"a/content/zh/blog/java/codeanalysis/3.0.8/10-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\346\263\250\345\206\214\344\270\255\345\277\203\351\205\215\347\275\256\344\277\241\346\201\257RegistryConfig.md"
 
"b/content/zh/blog/java/codeanalysis/3.0.8/10-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\346\263\250\345\206\214\344\270\255\345\277\203\351\205\215\347\275\256\344\277\241\346\201\257RegistryConfig.md"
new file mode 100644
index 00000000000..6e01a44e6a3
--- /dev/null
+++ 
"b/content/zh/blog/java/codeanalysis/3.0.8/10-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\346\263\250\345\206\214\344\270\255\345\277\203\351\205\215\347\275\256\344\277\241\346\201\257RegistryConfig.md"
@@ -0,0 +1,229 @@
+---
+title: "10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig"
+linkTitle: "10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig"
+date: 2022-08-10
+author: 宋小生
+description: >
+    [Dubbo 3.0.8源码解析] 
RegistryConfig注册中心配置包含了一些比较基础的注册信息相关的配置信息,注册中心是服务在分布式场景下的基础服务。
+---
+
+# 10-Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig
+## 10.1 简介
+先贴个代码用来参考:
+
+```java
+ DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+ bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+            .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+            .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+            .service(service)
+            .start()
+            .await();
+ 
+```
+
+上个博客我们说了启动器ApplicationConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码注册中心配置信息:
+```java
+registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+```
+
+
+## 10.2  注册中心的配置相关
+
+下面的配置来源于官网
+
+| 属性       | 对应URL参数          | 类型    | 是否必填 | 缺省值 | 作用     | 描述               
                                          | 兼容性         |
+| ---------- | -------------------- | ------- | -------- | ------ | -------- | 
------------------------------------------------------------ | -------------- |
+| id         |                      | string  | 可选     |        | 配置关联 | 
注册中心引用BeanId,可以在<dubbo:service registry="">或<dubbo:reference registry="">中引用此ID 
| 1.0.16以上版本 |
+| address    | <host:port>          | string  | **必填** |        | 服务发现 | 
注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔,如:ip:port,ip:port,不同集群的注册中心,请配置多个<dubbo:registry>标签
 | 1.0.16以上版本 |
+| protocol   | <protocol>           | string  | 可选     | dubbo  | 服务发现 | 
注册中心地址协议,支持`dubbo`, `multicast`, `zookeeper`, `redis`, `consul(2.7.1)`, 
`sofa(2.7.2)`, `etcd(2.7.2)`, `nacos(2.7.2)`等协议 | 2.0.0以上版本  |
+| port       | <port>               | int     | 可选     | 9090   | 服务发现 | 
注册中心缺省端口,当address没有带端口时使用此端口做为缺省值  | 2.0.0以上版本  |
+| username   | <username>           | string  | 可选     |        | 服务治理 | 
登录注册中心用户名,如果注册中心不需要验证可不填             | 2.0.0以上版本  |
+| password   | <password>           | string  | 可选     |        | 服务治理 | 
登录注册中心密码,如果注册中心不需要验证可不填               | 2.0.0以上版本  |
+| transport  | registry.transporter | string  | 可选     | netty  | 性能调优 | 
网络传输方式,可选mina,netty                                 | 2.0.0以上版本  |
+| timeout    | registry.timeout     | int     | 可选     | 5000   | 性能调优 | 
注册中心请求超时时间(毫秒)                                   | 2.0.0以上版本  |
+| session    | registry.session     | int     | 可选     | 60000  | 性能调优 | 
注册中心会话超时时间(毫秒),用于检测提供者非正常断线后的脏数据,比如用心跳检测的实现,此时间就是心跳间隔,不同注册中心实现不一样。 | 2.1.0以上版本  
|
+| file       | registry.file        | string  | 可选     |        | 服务治理 | 
使用文件缓存注册中心地址列表及服务提供者列表,应用重启时将基于此文件恢复,注意:两个注册中心不能使用同一文件存储 | 2.0.0以上版本  |
+| wait       | registry.wait        | int     | 可选     | 0      | 性能调优 | 
停止时等待通知完成时间(毫秒)                                 | 2.0.0以上版本  |
+| check      | check                | boolean | 可选     | true   | 服务治理 | 
注册中心不存在时,是否报错                                   | 2.0.0以上版本  |
+| register   | register             | boolean | 可选     | true   | 服务治理 | 
是否向此注册中心注册服务,如果设为false,将只订阅,不注册    | 2.0.5以上版本  |
+| subscribe  | subscribe            | boolean | 可选     | true   | 服务治理 | 
是否向此注册中心订阅服务,如果设为false,将只注册,不订阅    | 2.0.5以上版本  |
+| dynamic    | dynamic              | boolean | 可选     | true   | 服务治理 | 
服务是否动态注册,如果设为false,注册后将显示为disable状态,需人工启用,并且服务提供者停止时,也不会自动取消注册,需人工禁用。 | 
2.0.5以上版本  |
+| group      | group                | string  | 可选     | dubbo  | 服务治理 | 
服务注册分组,跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。 | 2.0.5以上版本  |
+| simplified | simplified           | boolean | 可选     | false  | 服务治理 | 
注册到注册中心的URL是否采用精简模式的(与低版本兼容)        | 2.7.0以上版本  |
+| extra-keys | extraKeys            | string  | 可选     |        | 服务治理 | 
在simplified=true时,extraKeys允许你在默认参数外将额外的key放到URL中,格式:“interface,key1,key2”。 | 
2.7.0以上版本  |
+
+
+
+ 同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:
+ 
+| 变量 | 类型 |    说明 |
+|--|--|--|
+| server |String  |
+| client |String  |
+| cluster |String  
|影响流量在注册中心之间的分布,在订阅多个注册中心时很有用,可用选项:1。区域感知,特定类型的流量总是根据流量的来源进入一个注册表。|
+| zone |String  |注册表所属的区域,通常用于隔离流量|
+| parameters |Map<String, String>  |自定义参数|
+| useAsConfigCenter |Boolean  |该地址是否用作配置中心|
+| useAsMetadataCenter |Boolean  |该地址是否用作远程元数据中心|
+| accepts |String  |此注册表接受的rpc协议列表,例如“dubbo,rest”|
+| preferred |Boolean  |如果设置为true,则始终首先使用此注册表,这在订阅多个注册表时非常有用|
+| weight |Integer  |影响注册中心之间的流量分布,当订阅多个注册中心仅在未指定首选注册中心时才生效时,此功能非常有用。|
+| registerMode |String  |注册模式:实例级,接口级,所有|
+| enableEmptyProtection |Boolean  |收到的空url地址列表和空保护被禁用,将清除当前可用地址|
+ 
+
+
+## 10.3 注册中心配置对象创建与添加
+前面例子中调用的代码
+```java
+.registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+```
+
+首先我们要来看的是RegistryConfig类型的构造器
+
+```java
+public RegistryConfig(String address) {
+        setAddress(address);
+ }
+```
+
+继续看setAddress方法
+
+```java
+ public void setAddress(String address) {
+               //保存地址
+        this.address = address;
+        //下面是支持将参数在url地址后面 比如用户名,密码,协议,端口,这几个参数提前做解析放入成员变量中
+        if (address != null) {
+            try {
+               //地址转Dubbo的URL对象 这个URL是Dubbo自行实现的URL封装信息的类型
+                URL url = URL.valueOf(address);
+
+                // Refactor since 2.7.8
+                //值不存在时候更新属性,非常巧妙的代码 重构了多个if判断
+                //第一个参数值不存在则调用第二个方法,第二个方法的参数为第三方方法                      
+                updatePropertyIfAbsent(this::getUsername, this::setUsername, 
url.getUsername());
+                updatePropertyIfAbsent(this::getPassword, this::setPassword, 
url.getPassword());
+                updatePropertyIfAbsent(this::getProtocol, this::setProtocol, 
url.getProtocol());
+                updatePropertyIfAbsent(this::getPort, this::setPort, 
url.getPort());
+
+                               //移除掉url中的backup自定义参数 (备份的注册中心地址)
+                Map<String, String> params = url.getParameters();
+                if (CollectionUtils.isNotEmptyMap(params)) {
+                    params.remove(BACKUP_KEY);
+                }
+                //将自定义参数存储到成员变量中
+                updateParameters(params);
+            } catch (Exception ignored) {
+            }
+        }
+    }
+```
+
+
+然后再回过头来看DubboBootstrap的registry方法:
+
+```java
+  public DubboBootstrap registry(RegistryConfig registryConfig) {
+        //将applicationModel对象设置给注册中心配置对象
+        registryConfig.setScopeModel(applicationModel);
+        //将注册中心配置对象添加到配置管理器中
+        configManager.addRegistry(registryConfig);
+        return this;
+    }
+```
+
+
+直接来看配置管理器configManager的添加注册中心配置addRegistry方法:
+
+```java
+public void addRegistry(RegistryConfig registryConfig) {
+    addConfig(registryConfig);
+}
+```
+
+configManager 的addConfig方法:
+
+```java
+public final <T extends AbstractConfig> T addConfig(AbstractConfig config) {
+        if (config == null) {
+            return null;
+        }
+        // ignore MethodConfig
+        //检查当前配置管理器支持管理的配置对象
+        //目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig,
+        
//ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig   
+      if (!isSupportConfigType(config.getClass())) {
+            throw new IllegalArgumentException("Unsupported config type: " + 
config);
+        }
+
+        if (config.getScopeModel() != scopeModel) {
+            config.setScopeModel(scopeModel);
+        }
+               //缓存中是否存在
+        Map<String, AbstractConfig> configsMap = 
configsCache.computeIfAbsent(getTagName(config.getClass()), type -> new 
ConcurrentHashMap<>());
+               //不是服务级接口配置则直接从缓存中读取到配置之后直接返回
+        // fast check duplicated equivalent config before write lock
+        if (!(config instanceof ReferenceConfigBase || config instanceof 
ServiceConfigBase)) {
+            for (AbstractConfig value : configsMap.values()) {
+                if (value.equals(config)) {
+                    return (T) value;
+                }
+            }
+        }
+
+        // lock by config type
+        //添加配置
+        synchronized (configsMap) {
+            return (T) addIfAbsent(config, configsMap);
+        }
+    }
+```
+
+
+
+ConfigManager配置管理器的addIfAbsent方法:
+
+```java
+private <C extends AbstractConfig> C addIfAbsent(C config, Map<String, C> 
configsMap)
+        throws IllegalStateException {
+               //配置信息为空直接返回
+        if (config == null || configsMap == null) {
+            return config;
+        }
+
+        // find by value
+        //根据配置规则判断,配置存在则返回
+        Optional<C> prevConfig = findDuplicatedConfig(configsMap, config);
+        if (prevConfig.isPresent()) {
+            return prevConfig.get();
+        }
+               
+               //生成配置key
+        String key = config.getId();
+        if (key == null) {
+            do {
+                // generate key if id is not set
+                key = generateConfigId(config);
+            } while (configsMap.containsKey(key));
+        }
+
+               //不相同的配置key重复则抛出异常
+        C existedConfig = configsMap.get(key);
+        if (existedConfig != null && !isEquals(existedConfig, config)) {
+            String type = config.getClass().getSimpleName();
+            logger.warn(String.format("Duplicate %s found, there already has 
one default %s or more than two %ss have the same id, " +
+                    "you can try to give each %s a different id, override 
previous config with later config. id: %s, prev: %s, later: %s",
+                type, type, type, type, key, existedConfig, config));
+        }
+
+        // override existed config if any
+        //将配置对象存入configsMap对象中,configsMap来源于configsCache
+        configsMap.put(key, config);
+        return config;
+    }
+```
+
+
+  原文: 
[<<Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig>>](https://blog.elastic.link/2022/07/10/dubbo/10-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-zhu-ce-zhong-xin-pei-zhi-xin-xi-registryconfig//)
\ No newline at end of file
diff --git 
"a/content/zh/blog/java/codeanalysis/3.0.8/11-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\345\215\217\350\256\256\351\205\215\347\275\256\344\277\241\346\201\257ProtocolConfig.md"
 
"b/content/zh/blog/java/codeanalysis/3.0.8/11-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\345\215\217\350\256\256\351\205\215\347\275\256\344\277\241\346\201\257ProtocolConfig.md"
new file mode 100644
index 00000000000..af4fd1d22e1
--- /dev/null
+++ 
"b/content/zh/blog/java/codeanalysis/3.0.8/11-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\345\215\217\350\256\256\351\205\215\347\275\256\344\277\241\346\201\257ProtocolConfig.md"
@@ -0,0 +1,109 @@
+---
+title: "11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig"
+linkTitle: "11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig"
+date: 2022-08-11
+author: 宋小生
+description: >
+    [Dubbo 3.0.8源码解析] ProtocolConfig协议配置是RPC调用过程中一些必要信息的基础。
+---
+
+# 11-Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig
+## 11.1 简介
+先贴个代码用来参考:
+
+```java
+ DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+ bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+            .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+            .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+            .service(service)
+            .start()
+            .await();
+ 
+```
+
+上个博客我们说了 RegistryConfig对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码协议配置信息:
+```java
+.protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+```
+
+
+## 11.2  协议的配置相关
+
+下面的配置来源于官网
+
+服务提供者协议配置。对应的配置类: org.apache.dubbo.config.ProtocolConfig。同时,如果需要支持多协议,可以声明多个 
<dubbo:protocol> 标签,并在 <dubbo:service> 中通过 protocol 属性指定使用的协议。
+
+| 属性          | 对应URL参数   | 类型           | 是否必填 | 缺省值                          
                             | 作用     | 描述                                      
                   | 兼容性         |
+| ------------- | ------------- | -------------- | -------- | 
------------------------------------------------------------ | -------- | 
------------------------------------------------------------ | -------------- |
+| id            |               | string         | 可选     | dubbo              
                                          | 配置关联 | 协议BeanId,可以在<dubbo:service 
protocol="">中引用此ID,如果ID不填,缺省和name属性值一样,重复则在name后加序号。 | 2.0.5以上版本  |
+| name          | <protocol>    | string         | **必填** | dubbo              
                                          | 性能调优 | 协议名称                         
                            | 2.0.5以上版本  |
+| port          | <port>        | int            | 可选     | 
dubbo协议缺省端口为20880,rmi协议缺省端口为1099,http和hessian协议缺省端口为80;如果**没有**配置port,则自动采用默认端口,如果配置为**-1**,则会分配一个没有被占用的端口。Dubbo
 2.4.0+,分配的端口在协议缺省端口的基础上增长,确保端口段可控。 | 服务发现 | 服务端口                               
                      | 2.0.5以上版本  |
+| host          | <host>        | string         | 可选     | 自动查找本机IP           
                                    | 服务发现 | 
-服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,-建议不要配置,让Dubbo自动获取本机IP | 2.0.5以上版本  |
+| threadpool    | threadpool    | string         | 可选     | fixed              
                                          | 性能调优 | 线程池类型,可选:fixed/cached        
                       | 2.0.5以上版本  |
+| threads       | threads       | int            | 可选     | 200                
                                          | 性能调优 | 服务线程池大小(固定大小)                
                     | 2.0.5以上版本  |
+| iothreads     | threads       | int            | 可选     | cpu个数+1            
                                        | 性能调优 | io线程池大小(固定大小)                  
                     | 2.0.5以上版本  |
+| accepts       | accepts       | int            | 可选     | 0                  
                                          | 性能调优 | 服务提供方最大可接受连接数                
                   | 2.0.5以上版本  |
+| payload       | payload       | int            | 可选     | 8388608(=8M)       
                                          | 性能调优 | 请求及响应数据包大小限制,单位:字节           
              | 2.0.5以上版本  |
+| codec         | codec         | string         | 可选     | dubbo              
                                          | 性能调优 | 协议编码方式                       
                          | 2.0.5以上版本  |
+| serialization | serialization | string         | 可选     | 
dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json | 性能调优 | 
协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json等
 | 2.0.5以上版本  |
+| accesslog     | accesslog     | string/boolean | 可选     |                    
                                          | 服务治理 | 
设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件 | 2.0.5以上版本  |
+| path          | <path>        | string         | 可选     |                    
                                          | 服务发现 | 提供者上下文路径,为服务path的前缀          
                 | 2.0.5以上版本  |
+| transporter   | transporter   | string         | 可选     | dubbo协议缺省为netty    
                                     | 性能调优 | 
协议的服务端和客户端实现类型,比如:dubbo协议的mina,netty等,可以分拆为server和client配置 | 2.0.5以上版本  |
+| server        | server        | string         | 可选     | 
dubbo协议缺省为netty,http协议缺省为servlet                  | 性能调优 | 
协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等 | 2.0.5以上版本  |
+| client        | client        | string         | 可选     | dubbo协议缺省为netty    
                                     | 性能调优 | 协议的客户端实现类型,比如:dubbo协议的mina,netty等 
         | 2.0.5以上版本  |
+| dispatcher    | dispatcher    | string         | 可选     | dubbo协议缺省为all      
                                     | 性能调优 | 
协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等 | 
2.1.0以上版本  |
+| queues        | queues        | int            | 可选     | 0                  
                                          | 性能调优 | 
线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。 | 
2.0.5以上版本  |
+| charset       | charset       | string         | 可选     | UTF-8              
                                          | 性能调优 | 序列化编码                        
                           | 2.0.5以上版本  |
+| buffer        | buffer        | int            | 可选     | 8192               
                                          | 性能调优 | 网络读写缓冲区大小                    
                       | 2.0.5以上版本  |
+| heartbeat     | heartbeat     | int            | 可选     | 0                  
                                          | 性能调优 | 
心跳间隔,对于长连接,当物理层断开时,比如拔网线,TCP的FIN消息来不及发送,对方收不到断开事件,此时需要心跳来帮助检查连接是否已断开 | 
2.0.10以上版本 |
+| telnet        | telnet        | string         | 可选     |                    
                                          | 服务治理 | 所支持的telnet命令,多个命令用逗号分隔       
                | 2.0.5以上版本  |
+| register      | register      | boolean        | 可选     | true               
                                          | 服务治理 | 该协议的服务是否注册到注册中心              
                 | 2.0.8以上版本  |
+| contextpath   | contextpath   | String         | 可选     | 缺省为空串              
                                     | 服务治理 |                                   
                           | 2.0.6以上版本  |
+
+
+ 同样官网提供的参数里面并未包含所有的属性 下面我就将其余的属性列举一下方便学习参考:
+ 
+| 变量 | 类型 |    说明 |
+|--|--|--|
+|threadname|String|线程池名称|
+|corethreads|Integer|线程池核心线程大小|
+|alive|Integer|线程池keepAliveTime,默认单位时间单位。毫秒|
+|exchanger|String|交换器配置信息如何交换|
+|prompt|String|命令行提示符|
+|status|String|状态检查|
+|sslEnabled|Boolean|ssl是否启用|
+
+
+## 11.3 协议配置对象创建与添加
+前面例子中调用的代码
+```java
+ .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+```
+这里我们配置了协议类型为dubbo 端口为-1则会分配一个没有被占用的端口
+
+继续看下DubboBootstrap的protocol方法
+
+```java
+public DubboBootstrap protocol(ProtocolConfig protocolConfig) {
+               //配置信息转List
+        return protocols(singletonList(protocolConfig));
+    }
+```
+
+继续看protocols方法 ,这个代码与前面两个博客中看到的向配置管理器添加配置对象的逻辑是一样的
+这里就不说了可以看前面的博客[《9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig》](https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/)
+```java
+   public DubboBootstrap protocols(List<ProtocolConfig> protocolConfigs) {
+        if (CollectionUtils.isEmpty(protocolConfigs)) {
+            return this;
+        }
+        for (ProtocolConfig protocolConfig : protocolConfigs) {
+            protocolConfig.setScopeModel(applicationModel);
+            configManager.addProtocol(protocolConfig);
+        }
+        return this;
+    }
+```
+
+原文:[Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig](https://blog.elastic.link/2022/07/10/dubbo/11-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-xie-yi-pei-zhi-xin-xi-protocolconfig/)
\ No newline at end of file
diff --git 
"a/content/zh/blog/java/codeanalysis/3.0.8/12-\345\205\250\345\261\200\350\247\206\351\207\216\346\235\245\347\234\213Dubbo3\347\232\204\346\234\215\345\212\241\345\220\257\345\212\250\347\224\237\345\221\275\345\221\250\346\234\237.md"
 
"b/content/zh/blog/java/codeanalysis/3.0.8/12-\345\205\250\345\261\200\350\247\206\351\207\216\346\235\245\347\234\213Dubbo3\347\232\204\346\234\215\345\212\241\345\220\257\345\212\250\347\224\237\345\221\275\345\221\250\346\234\237.md"
new file mode 100644
index 00000000000..935015f6671
--- /dev/null
+++ 
"b/content/zh/blog/java/codeanalysis/3.0.8/12-\345\205\250\345\261\200\350\247\206\351\207\216\346\235\245\347\234\213Dubbo3\347\232\204\346\234\215\345\212\241\345\220\257\345\212\250\347\224\237\345\221\275\345\221\250\346\234\237.md"
@@ -0,0 +1,349 @@
+---
+title: "12 全局视野来看Dubbo3的服务启动生命周期"
+linkTitle: "12 全局视野来看Dubbo3的服务启动生命周期"
+date: 2022-08-12
+author: 宋小生
+description: >
+    [Dubbo 3.0.8源码解析]  
全局视野来看Dubbo3的服务启动生命周期,了解了Dubbo3的启动生命周期,可以有效的了解整个Dubbo应用的启动阶段。
+---
+
+# 12 全局视野来看Dubbo3的服务启动生命周期
+## 12.1 启动方法简介
+在说启动方法之前先把视野拉回第一章[《1-从一个服务提供者的Demo说起》](https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/
 )我们的Demo代码,下面只贴一下核心代码:
+
+```java
+public class Application {
+    public static void main(String[] args) throws Exception {
+            startWithBootstrap();
+    }
+    private static void startWithBootstrap() {
+       //前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制
+        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
+        //为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了
+        service.setInterface(DemoService.class);
+        service.setRef(new DemoServiceImpl());
+        //这一个篇章主要说这里:
+        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+        //初始化应用配置
+        bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+        //初始化注册中心配置
+            .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+            //初始化协议配置
+            .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+            //初始化服务配置
+            .service(service)
+            //启动
+            .start()
+            .await();
+    }
+}
+```
+
+前面我们介绍了Dubbo启动器DubboBootstrap类型对象的创建,又介绍了为DubboBootstrap启动器初始化各种配置信息,这一个博客就开始到了分析启动方法的位置了,Dubbo启动器借助Deployer发布器来启动和发布服务,发布器的启动过程包含了启动配置中心,加载配置,启动元数据中心,启动服务等操作都是比较重要又比较复杂的过程,这里我们先来看下启动过程的生命周期来为后面的内容做好铺垫。
+
+## 12.2 启动器启动方法的调用逻辑start()
+这里我们就直接来看DubboBootstrap的start()方法:
+
+```java
+ public DubboBootstrap start() {
+               //调用重载的方法进行启动参数代表是否等待启动结束
+        this.start(true);
+        return this;
+    }
+```
+
+我们再来看重载的start方法:
+
+```java
+public DubboBootstrap start(boolean wait) {
+               
//这个发布器是在ApplicationModel对象创建之后初始化的时候进行初始化的具体类型为DefaultApplicationDeployer
+        Future future = applicationDeployer.start();
+      
+        if (wait) {
+            try {
+              //等待异步启动的结果
+                future.get();
+            } catch (Exception e) {
+               //启动失败则抛出一个异常
+                throw new IllegalStateException("await dubbo application start 
finish failure", e);
+            }
+        }
+        return this;
+    }
+```
+
+
+## 12.3 应用程序发布器DefaultApplicationDeployer的启动方法
+发布器是帮助我们发布服务和引用服务的,在Dubbo3中不论是服务提供者还是服务消费者如果想要启动服务都需要走这个启动方法的逻辑,所以务必重视
+
+我们直接来看DefaultApplicationDeployer的start()代码:
+
+```java
+@Override
+    public Future start() {
+       //启动锁,防止重复启动
+        synchronized (startLock) {
+            //发布器,状态已经设置为停止或者失败了就直接抛出异常
+            if (isStopping() || isStopped() || isFailed()) {
+                throw new IllegalStateException(getIdentifier() + " is 
stopping or stopped, can not start again");
+            }
+
+            try {
+                // maybe call start again after add new module, check if any 
new module
+                //可能在添加新模块后再次调用start,检查是否有任何新模块
+                //这里遍历当前应用程序下的所有模块如果某个模块是PENDING状态则这里hasPendingModule的值为true
+                boolean hasPendingModule = hasPendingModule();
+                               //发布器状态正在启动中
+                if (isStarting()) {
+                    // currently, is starting, maybe both start by module and 
application
+                    // if it has new modules, start them
+                    //存在挂器的模块
+                    if (hasPendingModule) {
+                       //启动模块
+                        startModules();
+                    }
+                    // if it is starting, reuse previous startFuture
+                    //模块异步启动中
+                    return startFuture;
+                }
+
+                // if is started and no new module, just return
+                //如果已启动且没有新模块,直接返回
+                if (isStarted() && !hasPendingModule) {
+                    return CompletableFuture.completedFuture(false);
+                }
+
+                // pending -> starting : first start app
+                // started -> starting : re-start app
+                //启动状态切换,将启动状态切换到STARTING(pending和started状态无需切换)
+                onStarting();
+                               //核心初始化逻辑,这里主要做一些应用级别启动比如配置中心,元数据中心
+                initialize();
+                               //启动模块(我们的服务提供和服务引用是在这个模块级别的)
+                doStart();
+            } catch (Throwable e) {
+                onFailed(getIdentifier() + " start failure", e);
+                throw e;
+            }
+
+            return startFuture;
+        }
+    }
+```
+
+这个启动方法逻辑不多 主要三个方法我们重点来看:
+- onStarting() 这个是启动之前的状态切换
+-  initialize()  应用的初始化逻辑 比如配置中心,元数据中心的初始化
+-  doStart() 启动模块比如启动我们的服务提供和服务引用的)
+
+继续看后面的细节吧,代码胜千言。
+
+## 12.4 应用程序发布器对应用级别的初始化逻辑
+这个我们先来看DefaultApplicationDeployer的初始化方法initialize():
+
+```java
+@Override
+    public void initialize() {
+       //状态判断 如果已经初始化过了就直接返回
+        if (initialized) {
+            return;
+        }
+        // Ensure that the initialization is completed when concurrent calls
+        //启动锁,确保在并发调用时完成初始化
+        synchronized (startLock) {
+               //双重校验锁 如果已经初始化过了就直接返回
+            if (initialized) {
+                return;
+            }
+            // register shutdown hook
+            
//注册关闭钩子,这个逻辑基本每个中间件应用都必须要要做的事情了,正常关闭应用回收资源,一般没这个逻辑情况下容易出现一些异常,让我们开发人员很疑惑,而这个逻辑往往并不好处理的干净。
+            registerShutdownHook();
+                       
+                       //启动配置中心,感觉Dubbo3耦合了这个玩意
+            startConfigCenter();
+                       
+                       //加载配置,一般配置信息当前机器的来源:环境变量,JVM启动参数,配置文字
+            loadApplicationConfigs();
+
+                       //初始化模块发布器 (发布服务提供和服务引用使用)
+            initModuleDeployers();
+
+            // @since 2.7.8
+            //启动元数据中心
+            startMetadataCenter();
+                       
+                       //初始化完成
+            initialized = true;
+
+            if (logger.isInfoEnabled()) {
+                logger.info(getIdentifier() + " has been initialized!");
+            }
+        }
+    }
+
+```
+
+这个是个生命周期整体概览的方法,将具体逻辑拆分到各个子方法中,是代码重构的一种策略,上面注释也很清楚了就不细说了,上面每个方法在后面会有单独的博客来分析。
+
+
+## 12.5 应用下模块的启动(服务的发布与引用)
+
+我们回过头来详细看DefaultApplicationDeployer的doStart()代码:
+
+```java
+private void doStart() {
+               // 启动模块
+        startModules();
+```
+
+DefaultApplicationDeployer的 startModules()方法
+```java
+private void startModules() {
+        // ensure init and start internal module first
+        //确保初始化并首先启动内部模块,Dubbo3中将模块分为内部和外部,内部是核心代码已经提供的一些服务比如元数据服务,外部是我们自己写的服务
+        prepareInternalModule();
+
+        // filter and start pending modules, ignore new module during 
starting, throw exception of module start
+        //启动所有的模块 (启动所有的服务)
+        for (ModuleModel moduleModel : new 
ArrayList<>(applicationModel.getModuleModels())) {
+               //这个状态默认就是PENDING的
+            if (moduleModel.getDeployer().isPending()) {
+               //模块启动器,发布服务
+                moduleModel.getDeployer().start();
+            }
+        }
+    }
+```
+
+这个模块的启动其实就是用来启动服务的 先启动内部服务,再启动外部服务
+内部服务有个元数据服务Dubbo3中每个服务都可以对外提供服务的元数据信息,来简化服务配置,不论是内部服务还是外部服务调用的代码逻辑都是模块发布器ModuleDeployer的start()方法,接下来我们详细看下模块发布器的生命周期函数。
+
+
+## 12.6 模块发布器发布服务的过程 
+
+前面我们说到了所有的服务都是经过模块发布器,ModuleDeployer的start()方法来启动的,那我们接下来就来看看这个模块发布器的启动方法。
+
+ModuleDeployer的start()方法代码:
+
+```java
+@Override
+    public synchronized Future start() throws IllegalStateException {
+       //模块发布器已经停止或者启动失败则直接抛出异常返回
+        if (isStopping() || isStopped() || isFailed()) {
+            throw new IllegalStateException(getIdentifier() + " is stopping or 
stopped, can not start again");
+        }
+
+        try {
+               //启动重或者已经启动了则直接返回一个Future对象 
+            if (isStarting() || isStarted()) {
+                return startFuture;
+            }
+                       //切换模块启动状态为STARTING
+            onModuleStarting();
+
+            // initialize
+            //如果应用未初始化则初始化(非正常逻辑)
+            applicationDeployer.initialize();
+            //模块发布器进行初始化
+            initialize();
+
+            // export services
+            //暴露服务
+            exportServices();
+
+            // prepare application instance
+            // exclude internal module to avoid wait itself
+            if (moduleModel != 
moduleModel.getApplicationModel().getInternalModule()) {
+                applicationDeployer.prepareInternalModule();
+            }
+
+            // refer services
+                       //引用服务
+            referServices();
+
+            // if no async export/refer services, just set started
+            //非异步启动则直接切换状态为STARTED
+            if (asyncExportingFutures.isEmpty() && 
asyncReferringFutures.isEmpty()) {
+                onModuleStarted();
+            } else {
+            //如果是异步的则等待服务发布和服务引用异步回调
+                frameworkExecutorRepository.getSharedExecutor().submit(() -> {
+                    try {
+                        // wait for export finish
+                        waitExportFinish();
+                        // wait for refer finish
+                        waitReferFinish();
+                    } catch (Throwable e) {
+                        logger.warn("wait for export/refer services occurred 
an exception", e);
+                    } finally {
+                       //异步回调完成 所有服务都启动了,再切换状态
+                        onModuleStarted();
+                    }
+                });
+            }
+        } catch (Throwable e) {
+            onModuleFailed(getIdentifier() + " start failed: " + e, e);
+            throw e;
+        }
+        return startFuture;
+    }
+```
+
+好了整体的服务启动生命周期就如上代码,后续我们再详细来看每个细节。
+
+
+
+## 12.7 发布器简介
+前面主要说了应用和模块的发布器的启动和初始化,下面简单了解下它们的关系,如下所示
+![在这里插入图片描述](https://img-blog.csdnimg.cn/37e7c05796ab4b38aa7658377e16c0aa.png)
+可以发布器主要包含 
+- 应用的发布器ApplicationDeployer用于初始化并启动应用程序实例
+- 模块发布器ModuleDeployer  模块(服务)的导出/引用服务
+
+两种发布器有各自的接口,他们都继承了抽象的发布器AbstractDeployer 封装了一些公共的操作比如状态切换,状态查询的逻辑。
+
+另外我们再来看下发布过程的状态枚举DeployState如下:
+
+```java
+public enum DeployState {
+    /**
+     * Unknown state
+     */
+    UNKNOWN,
+
+    /**
+     * Pending, wait for start
+     */
+    PENDING,
+
+    /**
+     * Starting
+     */
+    STARTING,
+
+    /**
+     * Started
+     */
+    STARTED,
+
+    /**
+     * Stopping
+     */
+    STOPPING,
+
+    /**
+     * Stopped
+     */
+    STOPPED,
+
+    /**
+     * Failed
+     */
+    FAILED
+}
+```
+
+
+Dubbo这一块后续可以优化以下,这里的状态切换全部耦合在一起了,可以考虑使用状态机将状态与行为解耦。
+
+ 
原文:[Dubbo启动器DubboBootstrap添加协议配置信息ProtocolConfig](https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/)
\ No newline at end of file
diff --git 
"a/content/zh/blog/java/codeanalysis/3.0.8/13-Dubbo\347\232\204\344\270\211\345\244\247\344\270\255\345\277\203\344\271\213\351\205\215\347\275\256\344\270\255\345\277\203.md"
 
"b/content/zh/blog/java/codeanalysis/3.0.8/13-Dubbo\347\232\204\344\270\211\345\244\247\344\270\255\345\277\203\344\271\213\351\205\215\347\275\256\344\270\255\345\277\203.md"
new file mode 100644
index 00000000000..7ddf5bee6ab
--- /dev/null
+++ 
"b/content/zh/blog/java/codeanalysis/3.0.8/13-Dubbo\347\232\204\344\270\211\345\244\247\344\270\255\345\277\203\344\271\213\351\205\215\347\275\256\344\270\255\345\277\203.md"
@@ -0,0 +1,430 @@
+
+# 13-Dubbo的三大中心之配置中心
+## 13.1 配置中心简介
+百度了一段不错的文字来介绍配置中心,我看了下肯定比我写的好多了,那我就直接拷贝过来一起看:
+
+*对于传统的单体应用而言,常使用配置文件来管理所有配置,比如SpringBoot的application.yml文件,但是在微服务架构中全部手动修改的话很麻烦而且不易维护。微服务的配置管理一般有以下需求:*
+- ***集中配置管理**,一个微服务架构中可能有成百上千个微服务,所以集中配置管理是很重要的。*
+- ***不同环境不同配置**,比如数据源配置在不同环境(开发,生产,测试)中是不同的。*
+- ***运行期间可动态调整**。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小等。*
+- ***配置修改后可自动更新**。如配置内容发生变化,微服务可以自动更新配置。*
+
+综上所述对于微服务架构而言,一套统一的,通用的管理配置机制是不可缺少的主要组成部分。常见的做法就是通过配置服务器进行管理。
+
+不过对于来看这个文章的小伙伴应该大部分对配置中心都会比较了解,分布式配置中心实现简单一点就是借助Zookeeper来协助存储,变更推送,不过为了实现各种不同的业务需求,市面上已经有很多很可靠的配置中心可用了,比如我从其他地方拷贝过来的图(虽然不是最新的但是可以供大家参考下):
+
+![在这里插入图片描述](/imgs/blog/source-blog/register.png)
+
+每个配置中心都有自己的实现,如果对配置中心感兴趣的小伙伴可以自行去对应开源项目官网查看,我们这里来看Dubbo对配置中心的支持
+
+***多配置中心:** Dubbo支持多配置中心,来 **保证其中一个配置中心集群出现不可用时能够切换到另一个配置中心集群** 
,保证能够正常从配置中心获取全局的配置、路由规则等信息。这也能够满足配置中心在部署上适应各类高可用的部署架构模式。-来自官网*
+
+做中间件可能考虑更多的的不仅仅是性能,还要过多的考虑高可用,高可用怎么做呢,其实就是失效转移,主备切换,降级,降级再降级这些理论的运用,多多考虑某一个服务挂了怎么办,Dubbo的多配置中心支持增加了复杂性,不过降低了服务不可用的风险,有一定的人手的公司还是值得做的。
+
+关于Dubbo的配置中心这里我来贴个官网的图:
+![在这里插入图片描述](/imgs/v3/concepts/centers-config.png)
+关于官网的介绍可以自行去官网看详细内容: 
[部署架构(注册中心、配置中心、元数据中心](https://dubbo.apache.org/zh/docs/concepts/registry-configcenter-metadata/)
+
+
+## 13.2 启动配置中心
+在上一个博客中说到了[《12-全局视野来看Dubbo3.0.8的服务启动生命周期》](https://blog.elastic.link/2022/07/10/dubbo/12-quan-ju-shi-ye-lai-kan-dubbo3.0.8-de-fu-wu-qi-dong-sheng-ming-zhou-qi/)Dubbo应用的启动过程DefaultApplicationDeployer的initialize()方法的全生命周期,在初始化方法中通过调用startConfigCenter();方法来启动配置中心的加载。后面就来详细看下:
+
+DefaultApplicationDeployer类型的startConfigCenter()代码如下:
+
+```java
+private void startConfigCenter() {
+
+        // load application config
+        //加载应用程序配置 (配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置)
+      configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class);
+
+        // try set model name
+        if (StringUtils.isBlank(applicationModel.getModelName())) {
+        //设置一下模块名字和模块描述(我们再Debug里面经常会看到这个描述信息 toString直接返回了Dubbo为我们改造的对象信息)
+            
applicationModel.setModelName(applicationModel.tryGetApplicationName());
+        }
+
+        // load config centers
+        //加载配置中心配置
+        //配置可能有多个地方可以配置需要遵循Dubbo约定的优先级进行设置,也可能是多应用,多注册中心这样的配置)
+        configManager.loadConfigsOfTypeFromProps(ConfigCenterConfig.class);
+               
//出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心
+        useRegistryAsConfigCenterIfNecessary();
+
+        // check Config Center
+        //配置管理器中获取配置中心
+        Collection<ConfigCenterConfig> configCenters = 
configManager.getConfigCenters();
+        //配置中心配置不为空则刷新配置中心配置将其放入配置管理器中
+        //下面开始刷新配置中心配置,如果配置中心配置为空则执行空刷新
+        if (CollectionUtils.isEmpty(configCenters)) {
+        //配置中心不存在的配置刷新
+            ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
+            configCenterConfig.setScopeModel(applicationModel);
+            configCenterConfig.refresh();
+            //验证配置
+            
ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
+            if (configCenterConfig.isValid()) {
+            //配置合法则将配置放入配置管理器中
+                configManager.addConfigCenter(configCenterConfig);
+                configCenters = configManager.getConfigCenters();
+            }
+        } else {
+        //一个或者多个配置中心配置存在的情况下的配置刷新
+            for (ConfigCenterConfig configCenterConfig : configCenters) {
+                configCenterConfig.refresh();
+                //验证配置
+                
ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
+            }
+        }
+
+               //配置中心配置不为空则将配置中心配置添加到environment中
+        if (CollectionUtils.isNotEmpty(configCenters)) {
+        //多配置中心本地动态配置对象创建CompositeDynamicConfiguration
+            CompositeDynamicConfiguration compositeDynamicConfiguration = new 
CompositeDynamicConfiguration();
+            //获取配置中心的相关配置
+            for (ConfigCenterConfig configCenter : configCenters) {
+                // Pass config from ConfigCenterBean to environment
+                //将配置中心的外部化配置,更新到环境里面
+                
environment.updateExternalConfigMap(configCenter.getExternalConfiguration());
+                //将配置中心的应用配置,添加到环境里面
+                
environment.updateAppExternalConfigMap(configCenter.getAppExternalConfiguration());
+
+                // Fetch config from remote config center
+                //从配置中心拉取配置添加到组合配置中
+                
compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
+            }
+            //将配置中心中的动态配置信息 设置到environment的动态配置属性中
+            environment.setDynamicConfiguration(compositeDynamicConfiguration);
+        }
+    }
+```
+
+### 13.2.1 配置管理器加载配置
+ 
+前面我们看到了配置管理器会从系统属性中加载配置这里我们来详细看下,配置往往是我们使用者比较关注的内容,
+```java
+configManager.loadConfigsOfTypeFromProps(ApplicationConfig.class);
+```
+
+配置管理器加载配置代码:
+来自ConfigManager的父类型AbstractConfigManager中
+```java
+public <T extends AbstractConfig> List<T> loadConfigsOfTypeFromProps(Class<T> 
cls) {
+        List<T> tmpConfigs = new ArrayList<>();
+        //获取属性配置 dubbo properties in classpath
+        //这个配置信息回头说
+        PropertiesConfiguration properties = 
environment.getPropertiesConfiguration();
+
+        // load multiple configs with id
+        //多注册中心配置id查询
+       
+       /*
+       搜索属性并提取指定类型的配置ID。
+       例如如下配置
+       # 配置信息 properties
+       dubbo.registries.registry1.address=xxx
+       dubbo.registries.registry2.port=xxx
+      
+       # 提取配置的id extract  
+       Set configIds = getConfigIds(RegistryConfig.class)
+      
+       # 提取的配置id结果 result
+       configIds: ["registry1", "registry2"]
+       */
+        Set<String> configIds = this.getConfigIdsFromProps(cls);
+        configIds.forEach(id -> {
+               //遍历这些配置id 判断配置缓存(configsCache成员变量)中是否已经存在当前配置
+            if (!this.getConfig(cls, id).isPresent()) {
+                T config;
+                try {
+                       //创建配置对象 为配置对象初始化配置id
+                    config = createConfig(cls, scopeModel);
+                    config.setId(id);
+                } catch (Exception e) {
+                    throw new IllegalStateException("create config instance 
failed, id: " + id + ", type:" + cls.getSimpleName());
+                }
+
+                String key = null;
+                boolean addDefaultNameConfig = false;
+                try {
+                    // add default name config (same as id), e.g. 
dubbo.protocols.rest.port=1234
+                    key = DUBBO + "." + AbstractConfig.getPluralTagName(cls) + 
"." + id + ".name";
+                    if (properties.getProperty(key) == null) {
+                        properties.setProperty(key, id);
+                        addDefaultNameConfig = true;
+                    }
+                               //刷新配置信息 好理解点就是Dubbo配置属性重写 
+                    config.refresh();
+                    //将当前配置信息添加到配置缓存中configsCache成员变量
+                    this.addConfig(config);
+                    tmpConfigs.add(config);
+                } catch (Exception e) {
+                    logger.error("load config failed, id: " + id + ", type:" + 
cls.getSimpleName(), e);
+                    throw new IllegalStateException("load config failed, id: " 
+ id + ", type:" + cls.getSimpleName());
+                } finally {
+                    if (addDefaultNameConfig && key != null) {
+                        properties.remove(key);
+                    }
+                }
+            }
+        });
+
+        // If none config of the type, try load single config
+        //如果没有该类型的配置,请尝试加载单个配置
+        if (this.getConfigs(cls).isEmpty()) {
+            // load single config
+            List<Map<String, String>> configurationMaps = 
environment.getConfigurationMaps();
+            if (ConfigurationUtils.hasSubProperties(configurationMaps, 
AbstractConfig.getTypePrefix(cls))) {
+                T config;
+                try {
+                    config = createConfig(cls, scopeModel);
+                    config.refresh();
+                } catch (Exception e) {
+                    throw new IllegalStateException("create default config 
instance failed, type:" + cls.getSimpleName());
+                }
+
+                this.addConfig(config);
+                tmpConfigs.add(config);
+            }
+        }
+
+        return tmpConfigs;
+    }
+```
+
+## 13.2.2  默认使用注册中心地址为配置中心
+出于兼容性目的,如果没有明确指定配置中心,并且registryConfig的UseAConfigCenter为null或true,请使用registry作为默认配置中心
+调用方法useRegistryAsConfigCenterIfNecessary()来处理逻辑
+我们来看下代码:
+
+```java
+private void useRegistryAsConfigCenterIfNecessary() {
+        // we use the loading status of DynamicConfiguration to decide whether 
ConfigCenter has been initiated.
+        
//我们使用DynamicConfiguration的加载状态来决定是否已启动ConfigCenter。配置中心配置加载完成之后会初始化动态配置defaultDynamicConfiguration
+        if (environment.getDynamicConfiguration().isPresent()) {
+            return;
+        }
+               //从配置缓存中查询是否存在config-center相关配置 ,如果已经存在配置了就无需使用注册中心的配置地址直接返回
+        if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) {
+            return;
+        }
+
+        // load registry
+        //加载注册中心相关配置
+        configManager.loadConfigsOfTypeFromProps(RegistryConfig.class);
+
+               //查询是否有注册中心设置了默认配置isDefault 
设置为true的注册中心则为默认注册中心列表,如果没有注册中心设置为默认注册中心,则获取所有未设置默认配置的注册中心列表
+        List<RegistryConfig> defaultRegistries = 
configManager.getDefaultRegistries();
+        //存在注册中心
+        if (defaultRegistries.size() > 0) {
+            defaultRegistries
+                .stream()
+                //判断当前注册中心是否可以作为配置中心
+                .filter(this::isUsedRegistryAsConfigCenter)
+                //将注册中心配置映射转换为配置中心
+                .map(this::registryAsConfigCenter)
+                //遍历配置中心流
+                .forEach(configCenter -> {
+                    if 
(configManager.getConfigCenter(configCenter.getId()).isPresent()) {
+                        return;
+                    }
+                    //配置管理器中添加配置中心,方便后去读取配置中心的配置信息
+                    configManager.addConfigCenter(configCenter);
+                    logger.info("use registry as config-center: " + 
configCenter);
+
+                });
+        }
+    }
+```
+
+#### 13.2.2.1 如何判断当前注册中心是否可以为配置中心
+isUsedRegistryAsConfigCenter
+
+```java
+private boolean isUsedRegistryAsCenter(RegistryConfig registryConfig, 
Supplier<Boolean> usedRegistryAsCenter,
+                                           String centerType,
+                                           Class<?> extensionClass) {
+        final boolean supported;
+                       //这个useAsConfigCenter参数是来自注册中心的配置 
如果配置了这个值则以这个值为准,如果配置了false则这个注册中心不能做为配置中心
+        Boolean configuredValue = usedRegistryAsCenter.get();
+        if (configuredValue != null) { // If configured, take its value.
+            supported = configuredValue.booleanValue();
+        } else {                       // Or check the extension existence
+               //这个逻辑的话是判断下注册中心的协议是否满足要求,我们例子代码中使用的是zookeeper
+            String protocol = registryConfig.getProtocol();
+            //这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 
protocol=xxxImpl
+            //动态配置的扩展类型为:interface 
org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
+            //zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 
,这个扩展类型为ZookeeperDynamicConfigurationFactory
+            
//代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory
+            supported = supportsExtension(extensionClass, protocol);
+            //配置中心走注册中心会打印一条日志
+            if (logger.isInfoEnabled()) {
+                logger.info(format("No value is configured in the registry, 
the %s extension[name : %s] %s as the %s center"
+                    , extensionClass.getSimpleName(), protocol, supported ? 
"supports" : "does not support", centerType));
+            }
+        }
+
+               //配置中心走注册中心会打印一条日志
+        if (logger.isInfoEnabled()) {
+            logger.info(format("The registry[%s] will be %s as the %s center", 
registryConfig,
+                supported ? "used" : "not used", centerType));
+        }
+        return supported;
+    }
+```
+这个扩展是否支持的逻辑判断是这样的扫描扩展类 看一下当前扩展类型是否有对应协议的扩展 比如在扩展文件里面这样配置过后是支持的 protocol=xxxImpl
+配置中心的动态配置的扩展类型为 
org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
+
+zookeeper协议肯定是支持的因为zookeeper协议实现了这个动态配置工厂 
,这个扩展类型为ZookeeperDynamicConfigurationFactory代码位置在dubbo-configcenter-zookeeper包中的org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory扩展配置中内容为
+```
+zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory
+```
+
+
+
+#### 13.2.2.2 注册中心配置转配置中心配置
+这个逻辑是registryAsConfigCenter方法,我来贴一下代码:
+
+```java
+private ConfigCenterConfig registryAsConfigCenter(RegistryConfig 
registryConfig) {
+               //注册中心协议获取这里例子中的是zookeeper协议
+        String protocol = registryConfig.getProtocol();
+        //注册中心端口 2181
+        Integer port = registryConfig.getPort();
+        //在Dubbo中配置信息 很多情况下都以URL形式表示,这里转换后的地址为zookeeper://127.0.0.1:2181
+        URL url = URL.valueOf(registryConfig.getAddress(), 
registryConfig.getScopeModel());
+        //生成当前配置中心的id 封装之后的内容为:
+        //config-center-zookeeper-127.0.0.1-2181
+        String id = "config-center-" + protocol + "-" + url.getHost() + "-" + 
port;
+        //配置中心配置对象创建
+        ConfigCenterConfig cc = new ConfigCenterConfig();
+        //config-center-zookeeper-127.0.0.1-2181
+        cc.setId(id);
+        cc.setScopeModel(applicationModel);
+        if (cc.getParameters() == null) {
+            cc.setParameters(new HashMap<>());
+        }
+        if (CollectionUtils.isNotEmptyMap(registryConfig.getParameters())) {
+            cc.getParameters().putAll(registryConfig.getParameters()); // copy 
the parameters
+        }
+        cc.getParameters().put(CLIENT_KEY, registryConfig.getClient());
+        //zookeeper
+        cc.setProtocol(protocol);
+        //2181
+        cc.setPort(port);
+        if (StringUtils.isNotEmpty(registryConfig.getGroup())) {
+            cc.setGroup(registryConfig.getGroup());
+        }
+        //这个方法转换地址是修复bug用的可以看bug https://github.com/apache/dubbo/issues/6476
+        cc.setAddress(getRegistryCompatibleAddress(registryConfig));
+        //注册中心分组做为配置中心命名空间 这里为null
+        cc.setNamespace(registryConfig.getGroup());
+        //zk认证信息
+        cc.setUsername(registryConfig.getUsername());
+         //zk认证信息
+        cc.setPassword(registryConfig.getPassword());
+        if (registryConfig.getTimeout() != null) {
+            cc.setTimeout(registryConfig.getTimeout().longValue());
+        }
+        //这个属性注释中已经建议了已经弃用了默认就是false了
+        //如果配置中心被赋予最高优先级,它将覆盖所有其他配置,
+        cc.setHighestPriority(false);
+        return cc;
+    }
+```
+
+
+## 13.3 配置刷新逻辑
+来自AbstractConfig类型的refresh()方法
+
+```java
+public void refresh() {
+        refreshed.set(true);
+        try {
+            // check and init before do refresh
+            //刷新之前执行的逻辑 这里并做什么逻辑
+            preProcessRefresh();
+               
+                       //获取当前域模型的环境信息对象
+            Environment environment = getScopeModel().getModelEnvironment();
+            List<Map<String, String>> configurationMaps = 
environment.getConfigurationMaps();
+
+            // Search props starts with PREFIX in order
+            String preferredPrefix = null;
+            for (String prefix : getPrefixes()) {
+                if (ConfigurationUtils.hasSubProperties(configurationMaps, 
prefix)) {
+                    preferredPrefix = prefix;
+                    break;
+                }
+            }
+            if (preferredPrefix == null) {
+                preferredPrefix = getPrefixes().get(0);
+            }
+            // Extract sub props (which key was starts with preferredPrefix)
+            Collection<Map<String, String>> instanceConfigMaps = 
environment.getConfigurationMaps(this, preferredPrefix);
+            Map<String, String> subProperties = 
ConfigurationUtils.getSubProperties(instanceConfigMaps, preferredPrefix);
+            InmemoryConfiguration subPropsConfiguration = new 
InmemoryConfiguration(subProperties);
+
+            if (logger.isDebugEnabled()) {
+                String idOrName = "";
+                if (StringUtils.hasText(this.getId())) {
+                    idOrName = "[id=" + this.getId() + "]";
+                } else {
+                    String name = ReflectUtils.getProperty(this, "getName");
+                    if (StringUtils.hasText(name)) {
+                        idOrName = "[name=" + name + "]";
+                    }
+                }
+                logger.debug("Refreshing " + this.getClass().getSimpleName() + 
idOrName +
+                    " with prefix [" + preferredPrefix +
+                    "], extracted props: " + subProperties);
+            }
+
+            assignProperties(this, environment, subProperties, 
subPropsConfiguration);
+
+            // process extra refresh of subclass, e.g. refresh method configs
+            processExtraRefresh(preferredPrefix, subPropsConfiguration);
+
+        } catch (Exception e) {
+            logger.error("Failed to override field value of config bean: " + 
this, e);
+            throw new IllegalStateException("Failed to override field value of 
config bean: " + this, e);
+        }
+
+        postProcessRefresh();
+    }
+```
+
+![在这里插入图片描述](/imgs/blog/source-blog/13-config-1.png)
+
+![在这里插入图片描述](/imgs/blog/source-blog/13-config2.png)
+
+
+
+
+
+## 13.4 配置中心配置大全
+ConfigCenterConfig类型
+ 下面配置信息来自官网
+dubbo:config-center 配置
+
+配置中心。对应的配置类:`org.apache.dubbo.config.ConfigCenterConfig`
+
+| 属性               | 对应URL参数            | 类型                | 是否必填 | 缺省值       
    | 描述                                                         | 兼容性 |
+| ------------------ | ---------------------- | ------------------- | -------- 
| ---------------- | 
------------------------------------------------------------ | ------ |
+| protocol           | config.protocol        | string              | 可选     | 
zookeeper        | 使用哪个配置中心:apollo、zookeeper、nacos等。 以zookeeper为例 1. 
指定protocol,则address可以简化为`127.0.0.1:2181`; 2. 
不指定protocol,则address取值为`zookeeper://127.0.0.1:2181` | 2.7.0+ |
+| address            | config.address         | string              | 必填     | 
                 | 配置中心地址。 取值参见protocol说明                          | 2.7.0+ |
+| highest-priority   | config.highestPriority | boolean             | 可选     | 
true             | 来自配置中心的配置项具有最高优先级,即会覆盖本地配置项。     | 2.7.0+ |
+| namespace          | config.namespace       | string              | 可选     | 
dubbo            | 通常用于多租户隔离,实际含义视具体配置中心而不同。 如: zookeeper - 环境隔离,默认值`dubbo`; 
apollo - 区分不同领域的配置集合,默认使用`dubbo`和`application` | 2.7.0+ |
+| cluster            | config.cluster         | string              | 可选     | 
                 | 含义视所选定的配置中心而不同。 如Apollo中用来区分不同的配置集群 | 2.7.0+ |
+| group              | config.group           | string              | 可选     | 
dubbo            | 含义视所选定的配置中心而不同。 nacos - 隔离不同配置集 zookeeper - 隔离不同配置集 | 2.7.0+ 
|
+| check              | config.check           | boolean             | 可选     | 
true             | 当配置中心连接失败时,是否终止应用启动。                     | 2.7.0+ |
+| config-file        | config.configFile      | string              | 可选     | 
dubbo.properties | 全局级配置文件所映射到的key zookeeper - 
默认路径/dubbo/config/dubbo/dubbo.properties apollo - dubbo 
namespace中的dubbo.properties键 | 2.7.0+ |
+| timeout            | config.timeout         | integer             |          
| 3000ms           | 获取配置的超时时间                                           | 
2.7.0+ |
+| username           |                        | string              |          
|                  | 如果配置中心需要做校验,用户名 Apollo暂未启用                | 2.7.0+ |
+| password           |                        | string              |          
|                  | 如果配置中心需要做校验,密码 Apollo暂未启用                  | 2.7.0+ |
+| parameters         |                        | Map<string, string> |          
|                  | 扩展参数,用来支持不同配置中心的定制化配置参数               | 2.7.0+ |
+| include-spring-env |                        | boolean             | 可选     | 
false            | 使用Spring框架时支持,为true时,会自动从Spring Environment中读取配置。 默认依次读取 
key为dubbo.properties的配置 key为dubbo.properties的PropertySource | 2.7.0+ |
+
+
+
+ 
原文:[Dubbo的三大中心之配置中心](https://blog.elastic.link/2022/07/10/dubbo/13-dubbo-de-san-da-zhong-xin-zhi-pei-zhi-zhong-xin-yuan-ma-jie-xi/)
\ No newline at end of file
diff --git 
"a/content/zh/blog/java/codeanalysis/3.0.8/8-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\345\200\237\345\212\251\345\217\214\351\207\215\346\240\241\351\252\214\351\224\201\347\232\204\345\215\225\344\276\213\346\250\241\345\274\217\350\277\233\350\241\214\345\257\271\350\261\241\347\232\204\345\210\235\345\247\213\345\214\226.md"
 
"b/content/zh/blog/java/codeanalysis/3.0.8/8-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\345\200\237\345\212\251\345\217\214\
 [...]
new file mode 100644
index 00000000000..0b35dd97068
--- /dev/null
+++ 
"b/content/zh/blog/java/codeanalysis/3.0.8/8-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\345\200\237\345\212\251\345\217\214\351\207\215\346\240\241\351\252\214\351\224\201\347\232\204\345\215\225\344\276\213\346\250\241\345\274\217\350\277\233\350\241\214\345\257\271\350\261\241\347\232\204\345\210\235\345\247\213\345\214\226.md"
@@ -0,0 +1,120 @@
+---
+title: "8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化"
+linkTitle: "8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化"
+date: 2022-08-08
+author: 宋小生
+description: >
+    [Dubbo 3.0.8源码解析] 启动器是为使用者提供简化的API功能来进行Dubbo应用的启动,手写Dubbo服务的启动使用启动器是必要的。
+---
+
+#  8-Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化
+## 8.1 启动器简介
+在说启动器之前先把视野拉回第一章[《1-从一个服务提供者的Demo说起》](https://blog.elastic.link/2022/07/10/dubbo/1-cong-yi-ge-demo-shuo-qi/
 )我们的Demo代码,下面只贴一下核心代码:
+
+```java
+public class Application {
+    public static void main(String[] args) throws Exception {
+            startWithBootstrap();
+    }
+    private static void startWithBootstrap() {
+       //前面的文章都在说这个服务配置对象的创建,中间又说了分层域模型,扩展加载机制
+        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
+        //为服务配置下服务接口和服务实现,下面两行用来初始化对象就不详细说了
+        service.setInterface(DemoService.class);
+        service.setRef(new DemoServiceImpl());
+        //这一个篇章主要说这里:
+        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+        bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+            .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+            .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+            .service(service)
+            .start()
+            .await();
+    }
+}
+```
+
+Dubbo3 
往云原生的方向走自然要针对云原生应用的应用启动,应用运行,应用发布等信息做一些建模,这个DubboBootstrap就是用来启动Dubbo服务的.类似于Netty的Bootstrap类型和ServerBootstrap启动器
+
+## 8.2 双重校验锁的单例模式创建启动器对象的
+Dubbo的bootstrap类为啥要用单例模式:
+
+通过调用静态方法getInstance()获取单例实例。之所以设计为单例,是因为Dubbo中的一些类(如ExtensionLoader)只为每个进程设计一个实例。
+
+下面就来直接看代码吧,代码胜千言:
+对象的调用代码如下:
+
+```java
+DubboBootstrap bootstrap = DubboBootstrap.getInstance();   
+```
+
+DubboBootstrap获取对象的getInstance()方法:
+```java
+ public static DubboBootstrap getInstance() {
+               //双重校验锁第一次判断空
+        if (instance == null) {
+               //为空都进行排队
+            synchronized (DubboBootstrap.class) {
+            //双重校验锁第二次判断空 上面为空的都排队了这里得判断下
+                if (instance == null) {
+                       //调用重载方法获取对象
+                    instance = 
DubboBootstrap.getInstance(ApplicationModel.defaultModel());
+                }
+            }
+        }
+        return instance;
+    }
+```
+
+DubboBootstrap获取对象重载的getInstance(ApplicationModel applicationModel)方法:
+
+*computeIfAbsent() 方法对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hashMap 中。*
+
+instanceMap设计为Map<ApplicationModel, DubboBootstrap>类型 
Key,意味着可以为多个应用程序模型创建不同的启动器,启动多个服务
+```java
+ public static DubboBootstrap getInstance(ApplicationModel applicationModel) {
+        return instanceMap.computeIfAbsent(applicationModel, _k -> new 
DubboBootstrap(applicationModel));
+    }
+```
+
+## 8.3 DubboBootstrap的构造器代码
+
+构造器代码是逻辑比较复杂的地方,我们先来看下代码
+
+```java
+private DubboBootstrap(ApplicationModel applicationModel) {
+               //存储应用程序启动模型
+        this.applicationModel = applicationModel;
+        //获取配置管理器ConfigManager:  配置管理器的扩展类型ApplicationExt ,扩展名字config
+        configManager = applicationModel.getApplicationConfigManager();
+        //获取环境信息Environment: 环境信息的扩展类型为ApplicationExt,扩展名字为environment
+        environment = applicationModel.getModelEnvironment();
+               //执行器存储仓库(线程池)ExecutorRepository: 
扩展类型为ExecutorRepository,默认扩展扩展名字为default
+        executorRepository = 
applicationModel.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+        //初始化并启动应用程序实例ApplicationDeployer,DefaultApplicationDeployer类型
+        applicationDeployer = applicationModel.getDeployer();
+        // listen deploy events
+        //为发布器 设置生命周期回调
+        applicationDeployer.addDeployListener(new 
DeployListenerAdapter<ApplicationModel>() {
+            @Override
+            public void onStarted(ApplicationModel scopeModel) {
+                notifyStarted(applicationModel);
+            }
+
+            @Override
+            public void onStopped(ApplicationModel scopeModel) {
+                notifyStopped(applicationModel);
+            }
+
+            @Override
+            public void onFailure(ApplicationModel scopeModel, Throwable 
cause) {
+                notifyStopped(applicationModel);
+            }
+        });
+        //将启动器对象注册到应用程序模型applicationModel的Bean工厂中
+        // register DubboBootstrap bean
+        applicationModel.getBeanFactory().registerBean(this);
+    }
+```
+
+ 原文: 
[<<Dubbo启动器DubboBootstrap借助双重校验锁的单例模式进行对象的初始化>>](https://blog.elastic.link/2022/07/10/dubbo/8-dubbo-qi-dong-qi-dubbobootstrap-jie-zhu-shuang-chong-xiao-yan-suo-de-dan-li-mo-shi-jin-xing-dui-xiang-de-chu-shi-hua/)
\ No newline at end of file
diff --git 
"a/content/zh/blog/java/codeanalysis/3.0.8/9-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\345\272\224\347\224\250\347\250\213\345\272\217\347\232\204\351\205\215\347\275\256\344\277\241\346\201\257ApplicationConfig.md"
 
"b/content/zh/blog/java/codeanalysis/3.0.8/9-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\345\272\224\347\224\250\347\250\213\345\272\217\347\232\204\351\205\215\347\275\256\344\277\241\346\201
 [...]
new file mode 100644
index 00000000000..1dc3416dedc
--- /dev/null
+++ 
"b/content/zh/blog/java/codeanalysis/3.0.8/9-Dubbo\345\220\257\345\212\250\345\231\250DubboBootstrap\346\267\273\345\212\240\345\272\224\347\224\250\347\250\213\345\272\217\347\232\204\351\205\215\347\275\256\344\277\241\346\201\257ApplicationConfig.md"
@@ -0,0 +1,194 @@
+---
+title: "9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig"
+linkTitle: "9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig"
+date: 2022-08-09
+author: 宋小生
+description: >
+    [Dubbo 3.0.8源码解析] ApplicationConfig应用配置包含了一些比较基础的配置信息。
+---
+
+# 9-Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig
+
+## 9.1 简介
+先贴个代码用来参考:
+
+```java
+ DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+ bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+            .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
+            .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
+            .service(service)
+            .start()
+            .await();
+```
+
+上个博客我们说了启动器对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码:
+
+```java
+bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+```
+
+## 9.2 应用程序ApplicationConfig的配置信息
+ApplicationConfig的构造器比较简单就是为他的成员变量name赋值来标识这个应用程序的名字
+下面我们直接参考下官网的配置表格:
+
+| 属性          | 对应URL参数         | 类型   | 是否必填 | 缺省值    | 作用     | 描述           
                                              | 兼容性         |
+| ------------- | ------------------- | ------ | -------- | --------- | 
-------- | ------------------------------------------------------------ | 
-------------- |
+| name          | application         | string | **必填** |           | 服务治理 | 
当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan
 | 1.0.16以上版本 |
+| version       | application.version | string | 可选     |           | 服务治理 | 
当前应用的版本                                               | 2.2.0以上版本  |
+| owner         | owner               | string | 可选     |           | 服务治理 | 
应用负责人,用于服务治理,请填写负责人公司邮箱前缀           | 2.0.5以上版本  |
+| organization  | organization        | string | 可选     |           | 服务治理 | 
组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等
 | 2.0.0以上版本  |
+| architecture  | architecture        | string | 可选     |           | 服务治理 | 
用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。 | 2.0.7以上版本  |
+| environment   | environment         | string | 可选     |           | 服务治理 | 
应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件 | 2.0.0以上版本  |
+| compiler      | compiler            | string | 可选     | javassist | 性能优化 | 
Java字节码编译器,用于动态类的生成,可选:jdk或javassist     | 2.1.0以上版本  |
+| logger        | logger              | string | 可选     | slf4j     | 性能优化 | 
日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk               | 2.2.0以上版本  |
+| metadata-type | metadata-type       | String | 可选     | local     | 服务治理 | 
metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有: remote - Provider 把 
metadata 放到远端注册中心,Consumer 从注册中心获取 local - Provider 把 metadata 放在本地,Consumer 从 
Provider 处直接获取 | 2.7.6以上版本  |
+
+
+官网的配置很详细了上面有一些属性是值得注意的比如这个name,compiler,logger,metadata-type 
我们可能要多看下默认值是什么,方便我们在使用过程中遇到问题的排查
+
+常用的属性参考官网的表格已经足够了,不过上面的属性不是列举了所有的属性,后续应该官方文档回更新:
+我这里把缺失的一些属性列举出来:
+
+| 变量 | 类型 |说明
+|--|--|--|
+|  registries|  List<RegistryConfig>|应用级注册中心列表|
+|registryIds|String|注册中心id列表|
+|monitor|MonitorConfig|应用级监控配置|
+|dumpDirectory|String|保存线程转储的目录|
+|qosEnable|Boolean|是否启用qos|
+|qosHost|String|要侦听的qos主机地址|
+|qosPort|Integer|要侦听的qos端口|
+|qosAcceptForeignIp|Boolean|qos是否接收外部IP|
+|parameters|Map<String, String>|自定义参数|
+|shutwait|String|应用程序关闭时间|赋值属性的时候会想系统属性dubbo.service.shutdown.wait里面存一份|
+|hostname|String|主机名|
+|registerConsumer|Boolean|用于控制是否将实例注册到注册表。仅当实例是纯消费者时才设置为“false”。|
+|repository|String|没找到哪里用了|
+|enableFileCache|Boolean|是否开启本地文件缓存|
+|protocol|String|此应用程序的首选协议(名称)适用于难以确定哪个是首选协议的地方|
+|metadataServiceProtocol|String|用于点对点的元数据传输的协议|
+|metadataServicePort|Integer|元数据服务端口号,用于服务发现|
+|livenessProbe|String|Liveness 存活探针 用于设置qos中探测器的扩展|
+|readinessProbe|String|Readiness 就绪探针|
+|startupProbe|String|Startup 启动探针|
+|registerMode|String|注册模式,实例级,接口集,所有|
+|enableEmptyProtection|Boolean|接收到的空url地址列表和空保护被禁用,将清除当前可用地址|
+
+这里我们先来简单了解下这个实体类型的基本配置,直接看配置可能不太好理解,后面我们讲到每个配置的时候可以回来参考一下
+
+
+
+## 应用程序配置对象添加到启动器中的配置管理器中
+了解了配置信息再回过头来看下这个配置信息如何存放到启动器里面的:
+
+我们的Demo调用代码如下:
+```java
+ DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+ bootstrap.application(new ApplicationConfig("dubbo-demo-api-provider"))
+```
+
+DubboBootstrap的application方法设置一个应用程序配置ApplicationConfig对象
+
+```java
+ public DubboBootstrap application(ApplicationConfig applicationConfig) {
+               //将启动器构造器中初始化的默认应用程序模型对象传递给配置对象
+        applicationConfig.setScopeModel(applicationModel);
+        //将配置信息添加到配置管理器中
+        configManager.setApplication(applicationConfig);
+        return this;
+    }
+```
+
+ConfigManager配置管理器的setApplication方法
+```java
+  @DisableInject
+    public void setApplication(ApplicationConfig application) {
+        addConfig(application);
+    }
+```
+
+ConfigManager配置管理器的addConfig方法
+```java
+public final <T extends AbstractConfig> T addConfig(AbstractConfig config) {
+               
+        if (config == null) {
+            return null;
+        }
+        // ignore MethodConfig
+        //检查当前配置管理器支持管理的配置对象
+        //目前支持的配置有ApplicationConfig,MonitorConfig,MetricsConfig,SslConfig,
+        //ProtocolConfig,RegistryConfig,ConfigCenterConfig,MetadataReportConfig
+        if (!isSupportConfigType(config.getClass())) {
+            throw new IllegalArgumentException("Unsupported config type: " + 
config);
+        }
+               
+        if (config.getScopeModel() != scopeModel) {
+            config.setScopeModel(scopeModel);
+        }
+               
+               //缓存中是否存在
+        Map<String, AbstractConfig> configsMap = 
configsCache.computeIfAbsent(getTagName(config.getClass()), type -> new 
ConcurrentHashMap<>());
+
+        // fast check duplicated equivalent config before write lock
+        //不是服务级配置则直接从缓存中读取到配置之后直接返回
+        if (!(config instanceof ReferenceConfigBase || config instanceof 
ServiceConfigBase)) {
+            for (AbstractConfig value : configsMap.values()) {
+                if (value.equals(config)) {
+                    return (T) value;
+                }
+            }
+        }
+
+        // lock by config type
+        //添加配置
+        synchronized (configsMap) {
+            return (T) addIfAbsent(config, configsMap);
+        }
+    }
+```
+
+
+ConfigManager配置管理器的addIfAbsent方法:
+
+```java
+private <C extends AbstractConfig> C addIfAbsent(C config, Map<String, C> 
configsMap)
+        throws IllegalStateException {
+               //配置信息为空直接返回
+        if (config == null || configsMap == null) {
+            return config;
+        }
+
+        // find by value
+        //根据配置规则判断,配置存在则返回
+        Optional<C> prevConfig = findDuplicatedConfig(configsMap, config);
+        if (prevConfig.isPresent()) {
+            return prevConfig.get();
+        }
+               
+               //生成配置key
+        String key = config.getId();
+        if (key == null) {
+            do {
+                // generate key if id is not set
+                key = generateConfigId(config);
+            } while (configsMap.containsKey(key));
+        }
+
+               //不相同的配置key重复则抛出异常
+        C existedConfig = configsMap.get(key);
+        if (existedConfig != null && !isEquals(existedConfig, config)) {
+            String type = config.getClass().getSimpleName();
+            logger.warn(String.format("Duplicate %s found, there already has 
one default %s or more than two %ss have the same id, " +
+                    "you can try to give each %s a different id, override 
previous config with later config. id: %s, prev: %s, later: %s",
+                type, type, type, type, key, existedConfig, config));
+        }
+
+        // override existed config if any
+        //将配置对象存入configsMap对象中,configsMap来源于configsCache
+        configsMap.put(key, config);
+        return config;
+    }
+```
+
+ 原文: 
[<<Dubbo启动器DubboBootstrap添加应用程序的配置信息ApplicationConfig>>](https://blog.elastic.link/2022/07/10/dubbo/9-dubbo-qi-dong-qi-dubbobootstrap-tian-jia-ying-yong-cheng-xu-de-pei-zhi-xin-xi-applicationconfig/)
\ No newline at end of file
diff --git a/static/imgs/blog/source-blog/13-config-1.png 
b/static/imgs/blog/source-blog/13-config-1.png
new file mode 100644
index 00000000000..2978fe31bc5
Binary files /dev/null and b/static/imgs/blog/source-blog/13-config-1.png differ
diff --git a/static/imgs/blog/source-blog/13-config2.png 
b/static/imgs/blog/source-blog/13-config2.png
new file mode 100644
index 00000000000..703150cbee8
Binary files /dev/null and b/static/imgs/blog/source-blog/13-config2.png differ
diff --git a/static/imgs/blog/source-blog/register.png 
b/static/imgs/blog/source-blog/register.png
new file mode 100644
index 00000000000..1d789629836
Binary files /dev/null and b/static/imgs/blog/source-blog/register.png differ

Reply via email to