Dubbo3 源码解读-宋小生-10:Dubbo启动器DubboBootstrap添加注册中心配置信息RegistryConfig

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: > Dubbo3 已经全面取代 HSF2 成为阿里的下一代服务框架,2022 双十一基于 Dubbo3 首次实现了关键业务不停推、不降级的全面用户体验提升,从技术上,大幅提高研发与运维效率的同时地址推送等关键资源利用率提升超 40%,基于三位一体的开源中间件体系打造了阿里在云上的单元化最佳实践和统一标准,同时将规模化实践经验与技术创新贡献开源社区,极大的推动了开源技术与标准的发展。> 本文是
Dubbo3 已经全面取代 HSF2 成为阿里的下一代服务框架,2022 双十一基于 Dubbo3 首次实现了关键业务不停推、不降级的全面用户体验提升,从技术上,大幅提高研发与运维效率的同时地址推送等关键资源利用率提升超 40%,基于三位一体的开源中间件体系打造了阿里在云上的单元化最佳实践和统一标准,同时将规模化实践经验与技术创新贡献开源社区,极大的推动了开源技术与标准的发展。

本文是 Dubbo 社区贡献者宋小生基于 Dubbo3 3.0.8 版本撰写的源码解析博客,在 Dubbo3 开源&内部技术栈统一的情况下,期望能对集团内的开发者了解 Dubbo3 背后的实现原理有所帮助。可点此查看 博客原文

本篇是宋小生系列 10/30 篇。同时,由 Dubbo3 团队领导的源码解读系列也正在进行中,感兴趣的同学可加入钉钉群了解详情: 28165003194

10.1 简介

先贴个代码用来参考:

 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对象的创建,启动器对象在启动之前是要初始化一些配置信息的,这里我们来看这一行代码注册中心配置信息:

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 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 int 可选 9090 服务发现 注册中心缺省端口,当address没有带端口时使用此端口做为缺省值 2.0.0以上版本
username string 可选 服务治理 登录注册中心用户名,如果注册中心不需要验证可不填 2.0.0以上版本
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 注册中心配置对象创建与添加

前面例子中调用的代码

.registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))

首先我们要来看的是RegistryConfig类型的构造器

public RegistryConfig(String address) {
        setAddress(address);
 }

继续看setAddress方法

 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方法:

  public DubboBootstrap registry(RegistryConfig registryConfig) {
        //将applicationModel对象设置给注册中心配置对象
        registryConfig.setScopeModel(applicationModel);
        //将注册中心配置对象添加到配置管理器中
        configManager.addRegistry(registryConfig);
        return this;
    }

直接来看配置管理器configManager的添加注册中心配置addRegistry方法:

public void addRegistry(RegistryConfig registryConfig) {
    addConfig(registryConfig);
}

configManager 的addConfig方法:

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方法:

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;
    }
相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
27天前
|
Dubbo 应用服务中间件 Apache
Dubbo 应用切换 ZooKeeper 注册中心实例,流量无损迁移
如果 Dubbo 应用使用 ZooKeeper 作为注册中心,现在需要切换到新的 ZooKeeper 实例,如何做到流量无损?
18 4
|
5月前
|
XML 监控 Dubbo
Dubbo怎么配置监控中心
**摘要:** 本文介绍了如何配置Dubbo的简单监控中心。首先,通过添加`&lt;dubbo:monitor protocol=&quot;registry&quot; /&gt;`到配置文件启用监控。接着,修改`dubbo.properties`设置Zookeeper地址。启动监控中心,服务提供者和消费者需添加`monitorEnabled=&quot;true&quot;`以开启监控功能。配置完成后,监控中心的Web界面能展示服务状态和性能指标,助力开发者和运维人员实时监控服务健康。
|
6月前
|
Dubbo Java 应用服务中间件
深度剖析:Dubbo使用Nacos注册中心的坑
2020年笔者在做微服务部件升级时,Dubbo的注册中心从Zookeeper切换到Nacos碰到个问题,最近刷Github又有网友提到类似的问题,就在这篇文章里做个梳理和总结。
深度剖析:Dubbo使用Nacos注册中心的坑
|
6月前
|
XML Cloud Native Dubbo
【Dubbo3高级特性】「提升系统安全性」手把手教你如何通过令牌进行Dubbo3服务验证及服务鉴权控制实战指南(一)
【Dubbo3高级特性】「提升系统安全性」手把手教你如何通过令牌进行Dubbo3服务验证及服务鉴权控制实战指南
313 1
|
6月前
|
Dubbo Cloud Native 应用服务中间件
【Dubbo3 终极特性】「云原生三中心架构」带你探索 Dubbo3 体系下的配置中心和元数据中心、注册中心的原理及开发实战(中)
【Dubbo3 终极特性】「云原生三中心架构」带你探索 Dubbo3 体系下的配置中心和元数据中心、注册中心的原理及开发实战(中)
193 1
|
6月前
|
XML Dubbo Java
【Dubbo3高级特性】「框架与服务」 Nacos作为注册中心-服务分组及服务分组聚合实现
【Dubbo3高级特性】「框架与服务」 Nacos作为注册中心-服务分组及服务分组聚合实现
272 0
|
6月前
|
XML Dubbo Java
【Dubbo3高级特性】「提升系统安全性」手把手教你如何通过令牌进行Dubbo3服务验证及服务鉴权控制实战指南(二)
【Dubbo3高级特性】「提升系统安全性」手把手教你如何通过令牌进行Dubbo3服务验证及服务鉴权控制实战指南
370 0
|
6月前
|
Kubernetes Dubbo 应用服务中间件
【Dubbo3终极特性】「流量治理体系」一文教你如何搭建Dubbo3的控制台服务Dubbo-Admin
【Dubbo3终极特性】「流量治理体系」一文教你如何搭建Dubbo3的控制台服务Dubbo-Admin
333 0
|
6月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
1月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
58 2