Open kylixs opened 3 years ago
在Dubbo 2中,属性配置与dubbo-spring-boot外部化配置的功能存在重叠,规则不清晰,在API配置、Spring XML配置、Java-config配置等不同场景下存在比较多的问题。
Dubbo 3对属性配置进行重新梳理,明确属性配置覆盖的规则,并融合外部化配置功能,贯通API配置、Spring XML配置、Java-config配置等不同场景。
属性配置覆盖是指用配置属性值覆盖config实例的字段值,类似Spring PropertyOverrideConfigurer的作用。 属性配置格式优先级(由高到低)如下:
PropertyOverrideConfigurer
#格式1 指定实例id的属性配置:复数前缀+config-id dubbo.{config-type}s.{config-id}.{config-item}={config-item-value} #格式2 指定实例name的属性配置:复数前缀+config-name (没有name属性时忽略此项) dubbo.{config-type}s.{config-name}.{config-item}={config-item-value} #格式3 单数配置(匿名实例配置):单数前缀 dubbo.{config-type}.{config-item}={config-item-value}
属性覆盖时,依照上面的次序查找匹配的属性,如果存在该种格式的属性,则选定此前缀来提取属性集合,其它的格式的配置将被忽略。举例说明:
@Configuration public class DubboConfig { @Bean public ProtocolConfig protocolConfig(){ ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setId("my-protocol"); protocolConfig.setName("dubbo"); return protocolConfig; } }
上面的ProtocolConfig属性覆盖查找顺序:
(1) 其id为"my-protocol", 尝试查找带id的配置: dubbo.protocols.my-protocol.xxx=xxx
(2) 如果没有找到匹配的属性配置,则继续找带name的配置(其name为"dubbo"):dubbo.protocols.dubbo.xxx=xxx
(3) 如果没有找到匹配的属性配置,则继续找单数配置:dubbo.protocol.xxx=xxx
注意: 按优先级高到低查找属性配置,如果找到匹配的属性配置,则使用此前缀打头的一组属性,不会再使用后面的格式。可以参照SpringBoot ConfigurationProperties的prefix来理解,不同的是Dubbo属性配置的prefix不是固定的,而是从多个prefix中选择存在的最高优先级的prefix。
外部化配置是指将DubboBootstrap API/Spring XML/Java-config等方式的config配置改为通过属性配置,即从配置属性生成config实例,类似SpringBoot ConfigurationProperties的作用。 下面是配置样例:
# application dubbo.application.name=demoapp # protocol dubbo.protocol.name=dubbo dubbo.protocol.port=20880 # registry dubbo.registry.address=zookeeper://10.10.10.1:2181 # config-center dubbo.config-center.address=zookeeper://10.10.10.2:2181 # consumer dubbo.consumer.check=false
多实例的配置样例:
#多个registry dubbo.registries.register1.address=zookeeper://10.10.10.1:2181 dubbo.registries.register2.address=zookeeper://10.10.10.2:2181 #多个protocol dubbo.protocols.dubbo.port=20881 dubbo.protocols.rest.port=8080
注意:Dubbo 3在内核支持了外部化配置功能,在非SpringBoot环境也可以正常使用。
属性配置覆盖 VS 外部化配置: (1) 外部化配置创建config实例时会检查是否存在相同id的config实例,如果不存在则创建,否则按照属性配置覆盖逻辑刷新原config实例的值; (2) 只有不存在某种类型的config实例时,才会根据单数配置创建config实例。
service 和reference的配置格式有所不同,按照下面的格式:
# 服务接口配置 dubbo.service.{interfaceName}.{item}={value} dubbo.reference.{interfaceName}.{item}={value} # 接口的方法配置 dubbo.service.{interfaceName}.{methodName}.{item}={value} dubbo.reference.{interfaceName}.{methodName}.{item}={value}
如DemoService接口的配置:
# 服务接口配置 dubbo.service.org.apache.dubbo.demo.DemoService.timeout=500 dubbo.reference.org.apache.dubbo.demo.DemoService.check=false # 接口的方法配置 dubbo.service.org.apache.dubbo.demo.DemoService.sayHello.timeout=500 dubbo.reference.org.apache.dubbo.demo.DemoService.sayHello.retries=3
复数配置的命名与普通单词变复数的规则相同: (1) 字母y结尾时,去掉y,改为ies (2) 字母s结尾时,加es (3) 其它加s
注意:service/reference的配置格式与其它不一样,为单数前缀+接口名。
Config的一些字段是内部使用,不能被配置的属性覆盖,刷新属性时会被忽略掉。 如:refreshed、valid、inited、prefixes、parentPrefix等,详细列表请查看org.apache.dubbo.config.AbstractConfig#IGNORED_ATTRIBUTES。
为了更透彻理解属性配置覆盖的功能,下面用几个例子进行分析说明。 下面几个例子都使用一份属性配置:
# default protocol config dubbo.protocol.name=dubbo dubbo.protocol.port=20883 # protocol config of name=dubbo dubbo.protocols.dubbo.port=20881 # protocol config of id=my-protocol dubbo.protocols.my-protocol.name=dubbo dubbo.protocols.my-protocol.port=20881
例子1(DubboBootstrap API):
DubboBootstrap.getInstance() ... .protocol(new ProtocolConfig()) ...
这里添加了一个空白的ProtocolConfig实例,没有设置id和name,根据上面的配置格式优先级,只能使用配置格式3(单数配置),如 dubbo.protocol.xxx=xxx 。
dubbo.protocol.xxx=xxx
#default protocol config dubbo.protocol.name=dubbo dubbo.protocol.port=20883
例子2(Spring XML):
<dubbo:protocol name="dubbo" port="20813"/>
这里通过XML定义了一个Protocol,并指定了name="dubbo",刚好匹配上配置格式2(带name的配置)。
# protocol config of name=dubbo dubbo.protocols.dubbo.port=20881
例子3(Spring Java-config):
@Configuration public class DubboConfig { @Bean("my-protocol") public ProtocolConfig protocolConfig(){ return new ProtocolConfig(); } }
这里定义了一个ProtocolConfig 的bean,没有在代码中指定id。当config id为空时,Dubbo会在BeanPostProcessor中将beanName设置为config实例的id。 根据上面的配置格式优先级,会优匹配带id的配置属性(dubbo.protocols.my-protocol.xxx=xxx)。
# protocol config of id=my-protocol dubbo.protocols.my-protocol.name=dubbo dubbo.protocols.my-protocol.port=20881
例子4:
@Configuration public class DubboConfig { @Bean public ProtocolConfig protocolConfig(){ return new ProtocolConfig("dubbo"); } }
这个例子与例子3的区别是ProtocolConfig的beanName为protocolConfig,会将其设置为ProtocolConfig的id。另外一个区别是设置是协议名称为"dubbo"。 属性查找过程: 1)尝试查找带id的属性:dubbo.protocols.protocolConfig.xxx=xxx,但没有找到这类属性; 2)尝试查找带name的属性:dubbo.protocols.dubbo.xxx=xxx,匹配上了第2组配置[protocol config of name=dubbo]
例子5:
<dubbo:protocol name="rest" port="8080"/>
属性查找过程: 1)这个例子的protocol指定name,但没有id(自动生成的beanName不会设置为config id,不会用于属性匹配); 2)查找带name的属性配置: dubbo.protocols.rest.xxx=xxx,但没有找到这种属性; 3)最后查找单数属性配置:dubbo.protocol.xxx=xxx
After Dubbo3.0, we need to provide a more comprehensive configuration description. https://github.com/apache/dubbo-website/issues/970
在Dubbo 2中,属性配置与dubbo-spring-boot外部化配置的功能存在重叠,规则不清晰,在API配置、Spring XML配置、Java-config配置等不同场景下存在比较多的问题。
Dubbo 3对属性配置进行重新梳理,明确属性配置覆盖的规则,并融合外部化配置功能,贯通API配置、Spring XML配置、Java-config配置等不同场景。
属性配置覆盖
属性配置覆盖是指用配置属性值覆盖config实例的字段值,类似Spring
PropertyOverrideConfigurer
的作用。 属性配置格式优先级(由高到低)如下:属性覆盖时,依照上面的次序查找匹配的属性,如果存在该种格式的属性,则选定此前缀来提取属性集合,其它的格式的配置将被忽略。举例说明:
上面的ProtocolConfig属性覆盖查找顺序:
(1) 其id为"my-protocol", 尝试查找带id的配置: dubbo.protocols.my-protocol.xxx=xxx
(2) 如果没有找到匹配的属性配置,则继续找带name的配置(其name为"dubbo"):dubbo.protocols.dubbo.xxx=xxx
(3) 如果没有找到匹配的属性配置,则继续找单数配置:dubbo.protocol.xxx=xxx
外部化配置
外部化配置是指将DubboBootstrap API/Spring XML/Java-config等方式的config配置改为通过属性配置,即从配置属性生成config实例,类似SpringBoot ConfigurationProperties的作用。 下面是配置样例:
多实例的配置样例:
属性配置覆盖 VS 外部化配置: (1) 外部化配置创建config实例时会检查是否存在相同id的config实例,如果不存在则创建,否则按照属性配置覆盖逻辑刷新原config实例的值; (2) 只有不存在某种类型的config实例时,才会根据单数配置创建config实例。
服务接口的配置
service 和reference的配置格式有所不同,按照下面的格式:
如DemoService接口的配置:
单复数配置对照表
复数配置的命名与普通单词变复数的规则相同: (1) 字母y结尾时,去掉y,改为ies (2) 字母s结尾时,加es (3) 其它加s
dubbo.reference.{interfaceName}.{methodName}.xxx=xxx
忽略的字段
Config的一些字段是内部使用,不能被配置的属性覆盖,刷新属性时会被忽略掉。 如:refreshed、valid、inited、prefixes、parentPrefix等,详细列表请查看org.apache.dubbo.config.AbstractConfig#IGNORED_ATTRIBUTES。
案例分析
为了更透彻理解属性配置覆盖的功能,下面用几个例子进行分析说明。 下面几个例子都使用一份属性配置:
例子1(DubboBootstrap API):
这里添加了一个空白的ProtocolConfig实例,没有设置id和name,根据上面的配置格式优先级,只能使用配置格式3(单数配置),如
dubbo.protocol.xxx=xxx
。例子2(Spring XML):
这里通过XML定义了一个Protocol,并指定了name="dubbo",刚好匹配上配置格式2(带name的配置)。
例子3(Spring Java-config):
这里定义了一个ProtocolConfig 的bean,没有在代码中指定id。当config id为空时,Dubbo会在BeanPostProcessor中将beanName设置为config实例的id。 根据上面的配置格式优先级,会优匹配带id的配置属性(dubbo.protocols.my-protocol.xxx=xxx)。
例子4:
这个例子与例子3的区别是ProtocolConfig的beanName为protocolConfig,会将其设置为ProtocolConfig的id。另外一个区别是设置是协议名称为"dubbo"。 属性查找过程: 1)尝试查找带id的属性:dubbo.protocols.protocolConfig.xxx=xxx,但没有找到这类属性; 2)尝试查找带name的属性:dubbo.protocols.dubbo.xxx=xxx,匹配上了第2组配置[protocol config of name=dubbo]
例子5:
属性查找过程: 1)这个例子的protocol指定name,但没有id(自动生成的beanName不会设置为config id,不会用于属性匹配); 2)查找带name的属性配置: dubbo.protocols.rest.xxx=xxx,但没有找到这种属性; 3)最后查找单数属性配置:dubbo.protocol.xxx=xxx