Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于Dubbo适配不同配置中心的存储和组织模型的问题 #3266

Closed
chickenlj opened this issue Jan 18, 2019 · 14 comments
Closed

关于Dubbo适配不同配置中心的存储和组织模型的问题 #3266

chickenlj opened this issue Jan 18, 2019 · 14 comments
Assignees

Comments

@chickenlj
Copy link
Contributor

目前,在2.7.0中,我们引入了配置中心的概念,并对接了Zookeeper, Apollo实现,并且不久将实现对接Nacos。
配置中心在Dubbo中扮演两个角色:

  1. 外部化配置。启动阶段Dubbo配置的集中式存储 (简单理解为dubbo.properties的外部化存储支持)。
  2. 服务治理。治理规则的存储和推送。
@chickenlj
Copy link
Contributor Author

chickenlj commented Jan 18, 2019

关于外部化存储,我们的对接模型上是将dubbo.properties作为一个整体考虑的,比如

  1. 对应到Zookeeper,是一个/dubbo/config/{全局或应用名}/dubbo.properties的节点,而节点下存储的是完整的.properties配置
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.metadataReport.address=zookeeper://127.0.0.1:2181
dubbo.protocol.port=-1
dubbo.consumer.timeout=600
  1. 对应到Apollo,我们从ZK的角度映射过来,只考虑{全局或应用名}/dubbo.properties的映射,全局或应用名对应到Apollo的global namespace或者application namespacedubbo.properties对应到apoll的key

但是,我们发现,这样的模型和Apollo的组织并不是很匹配,Apollo倾向于将一个namespace下的所有配置组织为一个.properties或其他格式的文件
http://106.12.25.204:8070/config.html?#/appid=dubbo-configcenter-apollo

我们可以考虑做如下改造:继续保持在Dubbo框架侧将dubbo.properties作为一个整体来操作和读取,而将dubbo.properties映射到Apollo时,不在作为一个namespace中的key,而是作为一个namespace将dubbo.properties中的配置内容分别作为NS中的key。看起来Apollo提供的ConfigService.getConfigFile能满足读取要求。

@chickenlj
Copy link
Contributor Author

chickenlj commented Jan 18, 2019

关于配置中心如何在Dubbo中工作的,可以参考Dubbo的OPS中的服务治理配置管理

@chickenlj
Copy link
Contributor Author

chickenlj commented Jan 18, 2019

Apollo中的入口API中,有三种方法:

ConfigSerfice.getConfig(String namespace) //获取NS下的配置,但是只提供每次一个key的读取,按这个模型我们需要将dubbo.properties整体作为一个key
ConfigService.getAppConfig() //同getconfig
ConfigService.getConfigFile(String namespace, ConfigFileFormat configFileFormat) //一次读取一个文件,支持.properties yaml json等?这个允许我们将dubbo.properties打散在一个NS中,又能实现一次性的读取和操作

不过看一下,Dubbo中的DynamicConfiguration API,都是以key为操作单位,适应一次性读取NS或许需要些改造
https://github.com/apache/incubator-dubbo/blob/master/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java

@nobodyiam
Copy link
Contributor

Apollo在设计之初就考虑到了公共组件的配置场景,下面摘抄一段之前在infoq上发表的文章

公共组件是指那些发布给其它应用使用的客户端代码,比如 RPC 客户端、DAL 客户端等。

这类组件一般是由单独的团队(如中间件团队)开发、维护,但是运行时是在业务实际应用内的,所以本质上可以认为是应用的一部分。

这类组件的特殊之处在于大部分的应用都会直接使用中间件团队提供的默认值,少部分的应用需要根据自己的实际情况对默认值进行调整。

比如数据库连接池的最小空闲连接数量(minimumIdle),出于对数据库资源的保护,DBA 要求将全公司默认的 minimumIdle 设为 1,对大部分的应用可能都适用,不过有些核心 / 高流量应用可能觉得太小,需要设为 10。

针对这种情况,可以借助于 Apollo 提供的 Namespace 实现:

  1. 中间件团队创建一个名为dal的公共 Namespace,设置全公司的数据库连接池默认配置
minimumIdle = 1
maximumPoolSize = 20
  1. dal 组件的代码会读取dal公共 Namespace 的配置
  1. 对大部分的应用由于默认配置已经适用,所以不用做任何事情
  1. 对于少量核心 / 高流量应用如果需要调整 minimumIdle 的值,只需要关联dal公共 Namespace,然后对需要覆盖的配置做调整即可,调整后的配置仅对该应用自己生效
minimumIdle = 10

image

通过这种方式的好处是不管是中间件团队,还是应用开发,都可以灵活地动态调整公共组件的配置。

更多说明可以参考Apollo核心概念之“Namespace”

所以,以apollo为例,对于一家公司内理想的配置使用方式可能是下面这样子的:

  1. 公共团队创建一个公共的namespace - dubbo,并设置全公司默认值
dubbo.registry.address=zookeeper://1.2.3.4:2181
dubbo.consumer.timeout=1000
  1. 所有dubbo的服务端和客户端引入dubbo-configcenter-apollo,并加入类似如下配置(106.12.25.204:8080为apollo meta server地址,支持逗号分隔填写多个,一般建议配置slb地址)
<dubbo:config-center address="apollo://106.12.25.204:8080"/>
  1. 对于大部分的应用其实不需要在apollo做任何额外配置就能正常工作

  2. 对于少量应用,比如有些觉得1秒的timeout太小了,需要设大一些,那么只要在自己项目中关联一下公共的dubbo namespace,然后覆盖dubbo.consumer.timeout即可,样例如下:

image

所以,apollo的模型是倾向于把namespace类比为properties文件,包含了逻辑上的一组配置,同时对于公共配置,支持通过界面直观的操作单个/多个配置的自定义。

按照目前dubbo的组织方式(把dubbo相关配置作为一个配置项管理)也能实现上述功能,不过对于用户使用(配置或覆盖)可能就不那么直观了。

鉴于dubbo需要接入更多配置中心,所以在模型上做一层抽象是合理的,不过也希望能讨论下是否有可能使这个抽象也能更好地支持类似apollo这样的模型。

@nobodyiam
Copy link
Contributor

@chickenlj

就API层面而言,apollo确实是支持获取单个key和整个文件两种不同用法的,以适配不同的使用场景,比如对上面提到的案例:

  1. 如果需要单独获取dubbo.consumer.timeout
ConfigService.getConfig("dubbo").getIntProperty("dubbo.consumer.timeout", defaultTimeout);
//返回1000
  1. 如果需要获取dubbo这个namespace所有的配置
ConfigService.getConfigFile("dubbo", ConfigFileFormat.Properties).getContent();
//以properties格式返回所有配置
//dubbo.registry.address=zookeeper\://1.2.3.4\:2181
//dubbo.consumer.timeout=5000

@chickenlj
Copy link
Contributor Author

所以,apollo的模型是倾向于把namespace类比为properties文件,包含了逻辑上的一组配置,同时对于公共配置,支持通过界面直观的操作单个/多个配置的自定义。

按照目前dubbo的组织方式(把dubbo相关配置作为一个配置项管理)也能实现上述功能,不过对于用户使用(配置或覆盖)可能就不那么直观了

@nobodyiam 我想我们已经就Apollo的模型Dubbo当前的抽象模型,这两个点上的理解达成了一致。

鉴于dubbo需要接入更多配置中心,所以在模型上做一层抽象是合理的,不过也希望能讨论下是否有可能使这个抽象也能更好地支持类似apollo这样的模型。

这是我开启这个issue讨论的原因之一,我们需要让Dubbo尽量遵循每个配置中心的原生概念。

  1. 如果需要获取dubbo这个namespace所有的配置
ConfigService.getConfigFile("dubbo", ConfigFileFormat.Properties).getContent();
//以properties格式返回所有配置
//dubbo.registry.address=zookeeper\://1.2.3.4\:2181
//dubbo.consumer.timeout=5000

ConfigService.getAppConfig()对应到ConfigService.getConfigFile("application", ConfigFileFormat.Properties).getContent();
是这样的吗?

@chickenlj
Copy link
Contributor Author

我加了这个PR,理论上能适应Apollo的模型,同时也能使用Nacos及Zookeeper的模型。
不过或许我们要在下一个迭代版本中发布了。#3271

@chickenlj
Copy link
Contributor Author

服务治理。治理规则的存储和推送。

关于服务治理部分,我发现使用中另一个模型匹配的问题,当前Dubbo的服务治理对Apollo的对接是:

  • 一个 key 对应一个 YAML文件(即治理规则)
  • 所有这些治理规则统一存储到叫dubbo的公共namespace

而如之前提,Apollo中的每个namespace都是一种配置文件类型,如properties json yaml等,那么就出现了namespace内嵌套yaml规则的问题,如这里所示:
http://106.12.25.204:8070/config.html?#/appid=dubbo-configcenter-apollo

@nobodyiam
Copy link
Contributor

ConfigService.getAppConfig()对应到ConfigService.getConfigFile("application", ConfigFileFormat.Properties).getContent(); 是这样的吗?

是的

关于服务治理部分,我发现使用中另一个模型匹配的问题,当前Dubbo的服务治理对Apollo的对接是:

  • 一个 key 对应一个 YAML文件(即治理规则)
  • 所有这些治理规则统一存储到叫dubbo的公共namespace

是的,目前看上去要么作为一个单独的namespace存在,要么作为一个namespace中的key存在

@cvictory cvictory self-assigned this May 17, 2019
@icom2014
Copy link

2.7.2 版本 使用apollo 作为配置中心后发现2个问题
1、仍然会去请求dubbo.properties 作为命名空间的配置,最后多生产了一个该命名空间的配置,结果与dubbo 命名空间的一样。这个配置重复,但不影响使用
2、应该是想去请求应用下的dubbo 的命名空间文件,希望覆盖吧,但请求了应用id 下命名空间=应用id 的命名空间,这个时候请求不到配置,出现多次重试,warn

@zhangyz-hd
Copy link
Contributor

zhangyz-hd commented Nov 25, 2019

@chickenlj 我们在尝试落地对接中,对于Apollo用于配置中心,仍存在一些疑惑

  1. namespace属性对应Apollo的namespace用于配置服务治理信息的,但和group、config-file、app-config-file的使用搭配缺乏明确的指导。
  2. 随着企业内服务数的增加(比如1万以上服务),namespace下的key-value数量是:服务数✖️2+节点数✖️4(1条提供者{application.name}、1条消费者{application.name}、1条{service:version:group}、1条消费者{application.name}.condition-router、1条{service:version:group}.condition-router、1条消费者{application.name}.tag-router),这个对Apollo整体性能容量、网络压力影响有多大?
  3. namespace下节点级的{application.name}.configurators和服务级的{service:version:group}.configurators的边界模糊。某些服务级别配置2个key下都能用。
  4. namespace中的key-value的key和value中yaml内的key存在意义冲突。

@chickenlj
Copy link
Contributor Author

  1. namespace属性对应Apollo的namespace用于配置服务治理信息的,但和group、config-file、app-config-file的使用搭配缺乏明确的指导。

对于 Apollo 的场景来说,config-file、app-config-file 并没有实际意义,只关注 namespace group 即可。推荐用法:<dubbo namespace="governance" group ="dubbo"/>

  1. 随着企业内服务数的增加(比如1万以上服务),namespace下的key-value数量是:服务数✖️2+节点数✖️4(1条提供者{application.name}、1条消费者{application.name}、1条{service:version:group}、1条消费者{application.name}.condition-router、1条{service:version:group}.condition-router、1条消费者{application.name}.tag-router),这个对Apollo整体性能容量、网络压力影响有多大?

因为通常需要配置规则的 service 还是少数的,并且 application 级别配置的引入一方面原因也是为了减少配置维护量。所以我认为实际和理论数据量还是会有些差异。

  1. namespace下节点级的{application.name}.configurators和服务级的{service:version:group}.configurators的边界模糊。某些服务级别配置2个key下都能用。

这个问题能否具体再描述一下?

  1. namespace中的key-value的key和value中yaml内的key存在意义冲突。

能否举个例子。因为按照设计,value中yaml内的key 是不暴露到用户侧的,由 Dubbo-Admin 来自动填充,屏蔽掉 key-value的keyvalue中yaml内的key 的转换和差异。

@zhangyz-hd
Copy link
Contributor

  1. namespace下节点级的{application.name}.configurators和服务级的{service:version:group}.configurators的边界模糊。某些服务级别配置2个key下都能用。

这个问题能否具体再描述一下?

比如

configVersion: v.27
scope: service
key: org.apache.dubbo.demo.DemoService
configs:
  - parameters:
    weight: 500
    side: consumer

这条配置,既可以写在key=org.apache.dubbo.demo.DemoService::.configurators下,也可以写在key=demo-consumer.configurators下。因为在RegistryDirectory.overrideWithConfigurator()中都处理了。

@zhangyz-hd
Copy link
Contributor

zhangyz-hd commented Nov 27, 2019

  1. namespace属性对应Apollo的namespace用于配置服务治理信息的,但和group、config-file、app-config-file的使用搭配缺乏明确的指导。

对于 Apollo 的场景来说,config-file、app-config-file 并没有实际意义,只关注 namespace group 即可。推荐用法:<dubbo namespace="governance" group ="dubbo"/>

从AbstractInterfaceConfig.prepareEnviroment的设计看,我理解整体上其实需要的是3份配置文件(先不管代码逻辑)

  1. app级别(节点级别),对应Environment.appExternalConfigs,应该读取app-config-file/config-file参数值,默认值=application
  2. dubbo级别(组件级别),对应Environment.externalConfigs,应该读取group参数值,默认值=dubbo
  3. 治理规则级别,对应各个Router和override监听,目前读取namespace参数值,默认值=governance
    我不知道这样理解是否正确?至少这样调理可以更清晰。同时参数名做调整也更好了:
<dubbo:config-center app-config-file="application" group="dubbo" rules="governance">

那么再进一步归纳,需要的其实是2类配置

  1. 启动时加载的参数,key=value(string),存储到Environment,可以对应多个Apollo上的namespace。
  2. 规则参数,key=value(yaml),对应Apollo上另外一个namespace。

是不是就简化为:

<dubbo:config-center configs="application; dubbo;" rules="governance">

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants