Apollo配置中心.docx
《Apollo配置中心.docx》由会员分享,可在线阅读,更多相关《Apollo配置中心.docx(29页珍藏版)》请在冰豆网上搜索。
Apollo配置中心
Apollo配置中心
资料来源:
更多资料请参考:
设计
一总体设计
1基础模型
2架构模块
3各模块概要介绍
1ConfigService
∙提供配置获取接口
∙提供配置更新推送接口(基于Httplongpolling)
o服务端使用SpringDeferredResult实现异步化,从而大大增加长连接数量
o目前使用的tomcatembed默认配置是最多10000个连接(可以调整),使用了4C8G的虚拟机实测可以支撑10000个连接,所以满足需求(一个应用实例只会发起一个长连接)。
∙接口服务对象为Apollo客户端
2AdminService
∙提供配置管理接口
∙提供配置修改、发布等接口
∙接口服务对象为Portal
3MetaServer
∙Portal通过域名访问MetaServer获取AdminService服务列表(IP+Port)
∙Client通过域名访问MetaServer获取ConfigService服务列表(IP+Port)
∙MetaServer从Eureka获取ConfigService和AdminService的服务信息,相当于是一个EurekaClient
∙增设一个MetaServer的角色主要是为了封装服务发现的细节,对Portal和Client而言,永远通过一个Http接口获取AdminService和ConfigService的服务信息,而不需要关心背后实际的服务注册和发现组件
∙MetaServer只是一个逻辑角色,在部署时和ConfigService是在一个JVM进程中的
4Eureka
∙基于Eureka和SpringCloudNetflix提供服务注册和发现
∙ConfigService和AdminService会向Eureka注册服务,并保持心跳
∙为了简单起见,目前Eureka在部署时和ConfigService是在一个JVM进程中的(通过SpringCloudNetflix)
5Portal
∙提供Web界面供用户管理配置
∙通过MetaServer获取AdminService服务列表(IP+Port),通过IP+Port访问服务
∙在Portal侧做loadbalance、错误重试
6Client
∙Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能
∙通过MetaServer获取ConfigService服务列表(IP+Port),通过IP+Port访问服务
∙在Client侧做loadbalance、错误重试
二、服务端设计
1配置发布后的实时推送设计
在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。
下面我们简要看一下这块是怎么设计实现的。
上图简要描述了配置发布的大致过程:
1.用户在Portal操作配置发布
2.Portal调用AdminService的接口操作发布
3.AdminService发布配置后,发送ReleaseMessage给各个ConfigService
4.ConfigService收到ReleaseMessage后,通知对应的客户端
2发送ReleaseMessage的实现方式
AdminService在配置发布后,需要通知所有的ConfigService有配置发布,从而ConfigService可以通知对应的客户端来拉取最新的配置。
从概念上来看,这是一个典型的消息使用场景,AdminService作为producer发出消息,各个ConfigService作为consumer消费消息。
通过一个消息组件(MessageQueue)就能很好的实现AdminService和ConfigService的解耦。
在实现上,考虑到Apollo的实际使用场景,以及为了尽可能减少外部依赖,我们没有采用外部的消息中间件,而是通过数据库实现了一个简单的消息队列。
实现方式如下:
1.AdminService在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace,参见DatabaseMessageSender
2.ConfigService有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见ReleaseMessageScanner
3.ConfigService如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2,消息监听器的注册过程参见ConfigServiceAutoConfiguration
4.NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端
示意图如下:
2.1.2ConfigService通知客户端的实现方式
上一节中简要描述了NotificationControllerV2是如何得知有配置发布的,那NotificationControllerV2在得知有配置发布后是如何通知到客户端的呢?
实现方式如下:
1.客户端会发起一个Http请求到ConfigService的notifications/v2接口,也就是NotificationControllerV2,参见RemoteConfigLongPollService
2.NotificationControllerV2不会立即返回结果,而是通过SpringDeferredResult把请求挂起
3.如果在30秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端
4.如果有该客户端关心的配置发布,NotificationControllerV2会调用DeferredResult的setResult方法,传入有配置变化的namespace信息,同时该请求会立即返回。
客户端从返回的结果中获取到配置变化的namespace后,会立即请求ConfigService获取该namespace的最新配置。
三、客户端设计
上图简要描述了Apollo客户端的实现原理:
1.客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。
(通过HttpLongPolling实现)
2.客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
o这是一个fallback机制,为了防止推送机制失效导致配置不更新
o客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304-NotModified
o定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定SystemProperty:
apollo.refreshInterval来覆盖,单位为分钟。
3.客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
4.客户端会把从服务端获取到的配置在本地文件系统缓存一份
o在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
5.应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知
Java客户端应用
一、准备工作
1.1环境要求
∙Java:
1.7+
∙Guava:
15.0+
oApollo客户端默认会引用Guava19,如果你的项目引用了其它版本,请确保版本号大于等于15.0
1.2必选设置
Apollo客户端依赖于AppId,Environment等环境信息来工作
1.2.1AppId
AppId是应用的身份信息,是从服务端获取配置的一个重要信息。
请确保classpath:
/META-INF/app.properties文件存在,并且其中内容形如:
app.id=YOUR-APP-ID
文件位置参考如下:
也可通过SystemProperty传入app.id信息,如
-Dapp.id=YOUR-APP-ID
注:
app.id是用来标识应用身份的唯一id,格式为string。
1.2.2Environment
Apollo支持应用在不同的环境有不同的配置,所以Environment是另一个从服务器获取配置的重要信息。
Environment可以通过以下3种方式的任意一个配置:
1.通过JavaSystemProperty
o可以通过Java的SystemPropertyenv来指定环境
o在Java程序启动脚本中,可以指定-Denv=YOUR-ENVIRONMENT
▪如果是运行jar文件,需要注意格式是java-Denv=YOUR-ENVIRONMENT-jarxxx.jar
o注意key为全小写
2.通过操作系统的SystemEnvironment
o还可以通过操作系统的SystemEnvironmentENV来指定
o注意key为全大写
3.通过配置文件
o最后一个推荐的方式是通过配置文件来指定env=YOUR-ENVIRONMENT
o对于Mac/Linux,文件位置为/opt/settings/server.properties
o对于Windows,文件位置为C:
\opt\settings\server.properties
文件内容形如:
env=DEV
目前,env支持以下几个值(大小写不敏感)
∙DEV
∙FAT
∙UAT(目前不设UAT环境)
∙PRO
注:
运行时会通过env信息匹配metaserver信息,比如env是DEV的话,就会读取apollo-core.jar中apollo-env.properties中的dev_meta信息。
如果希望在运行时覆盖metaserver信息,有两个方法:
1.通过-D{env}_meta=xxx来覆盖,如-Ddev_meta=
2.准备一份单独的apollo-env.properties,放在程序运行的classpath中,或者放在程序运行目录下,具体可以参考apollo-env.properties的加载逻辑-ResourceUtils和apollo-env.properties原始文件,需要注意的是在apollo-env.properties中需要用形如{env}.meta=xxx来覆盖,如dev.meta=。
1.2.3本地缓存路径
Apollo客户端会把从服务端获取到的配置在本地文件系统缓存一份,用于在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置,不影响应用正常运行。
本地缓存路径位于以下路径,所以请确保/opt/data或C:
\opt\data\目录存在,且应用有读写权限。
∙Mac/Linux:
/opt/data/{appId}/config-cache
∙Windows:
C:
\opt\data\{appId}\config-cache
本地配置文件会以下面的文件名格式放置于本地缓存路径下:
{appId}+{cluster}+{namespace}.properties
∙appId就是应用自己的appId,如
∙cluster就是应用使用的集群,一般在本地模式下没有做过配置的话,就是default
∙namespace就是应用使用的配置namespace,一般是application
文件内容以properties格式存储,比如如果有两个key,一个是request.timeout,另一个是batch,那么文件内容就是如下格式:
request.timeout=2000
batch=2000
注:
本地缓存路径也可用于容灾目录,如果应用在所有configservice都挂掉的情况下需要扩容,那么也可以先把配置从已有机器上的缓存路径复制到新机器上的相同缓存路径
1.2.4可选设置
Cluster(集群)
Apollo支持配置按照集群划分,也就是说对于一个appId和一个环境,对不同的集群可以有不同的配置。
如果需要使用这个功能,你可以通过以下方式来指定运行时的集群:
1.通过JavaSystemProperty
o我们可以通过Java的SystemProperty设置apollo.cluster来指定运行时集群(注意key为全小写)
o例如,可以在程序启动时通过-Dapollo.cluster=SomeCluster来指定运行时的集群为SomeCluster
2.通过配置文件
o首先确保/opt/settings/server.properties(Mac/Linux)或C:
\opt\settings\server.properties(Windows)在目标机器上存在
o在这个文件中,可以设置数据中心集群,如idc=xxx
o注意key为全小写
ClusterPrecedence(集群顺序)
1.如果apollo.cluster和idc同时指定:
o我们会首先尝试从apollo.cluster指定的集群加载配置
o如果没找到任何配置,会尝试从idc指定的集群加载配置
o如果还是没找到,会从默认的集群(default)加载
2.如果只指定了apollo.cluster:
o我们会首先尝试从apollo.cluster指定的集群加载配置
o如果没找到,会从默认的集群(default)加载
3.如果只指定了idc:
o我们会首先尝试从idc指定的集群加载配置
o如果没找到,会从默认的集群(default)加载
4.如果apollo.cluster和idc都没有指定:
o我们会从默认的集群(default)加载配置
二、MavenDependency
由于客户端jar包中会包含metaserver信息,无法上传一个统一的jar包到中央仓库,所以请按照分布式部署指南的文档说明打包并上传到自己公司的Maven私服。
应用在实际使用时只需要按照如下方式引入即可。
apollo-client
三、客户端用法
Apollo支持API方式和Spring整合方式,该怎么选择用哪一种方式?
∙API方式灵活,功能完备,配置值实时更新(热发布),支持所有Java环境。
∙Spring方式接入简单,结合Spring有N种酷炫的玩法,如
oPlaceholder方式:
▪代码中使用,如:
@Value("${someKeyFromApollo:
someDefaultValue}")
▪application.properties中使用,如:
oSpringboot的@ConfigurationProperties方式
o缺点是这类注入的值在变化后不会重新注入,需要重启才会更新,如果需要配置值实时更新,可以参考后续3.2.2SpringPlaceholder的使用的说明
∙Spring方式也可以结合API方式使用,如注入Apollo的Config对象,就可以照常通过API方式获取配置了:
∙@ApolloConfig
privateConfigconfig;//injectconfigfornamespaceapplication
3.1API使用方式
API方式是最简单、高效使用Apollo配置的方式,不依赖Spring框架即可使用。
3.1.1获取默认namespace的配置(application)
Configconfig=ConfigService.getAppConfig();//configinstanceissingletonforeachnamespaceandisnevernull
StringsomeKey="someKeyFromDefaultNamespace";
StringsomeDefaultValue="someDefaultValueForTheKey";
Stringvalue=config.getProperty(someKey,someDefaultValue);
通过上述的config.getProperty可以获取到someKey对应的实时最新的配置值。
另外,配置值从内存中获取,所以不需要应用自己做缓存。
3.1.2监听配置变化事件
监听配置变化事件只在应用真的关心配置变化,需要在配置变化时得到通知时使用,比如:
数据库连接串变化后需要重建连接等。
如果只是希望每次都取到最新的配置的话,只需要按照上面的例子,调用config.getProperty即可。
Configconfig=ConfigService.getAppConfig();//configinstanceissingletonforeachnamespaceandisnevernull
config.addChangeListener(newConfigChangeListener(){
@Override
publicvoidonChange(ConfigChangeEventchangeEvent){
System.out.println("Changesfornamespace"+changeEvent.getNamespace());
for(Stringkey:
changeEvent.changedKeys()){
ConfigChangechange=changeEvent.getChange(key);
System.out.println(String.format("Foundchange-key:
%s,oldValue:
%s,newValue:
%s,changeType:
%s",change.getPropertyName(),change.getOldValue(),change.getNewValue(),change.getChangeType()));
}
}
});
3.1.3获取公共Namespace的配置
StringsomePublicNamespace="CAT";
Configconfig=ConfigService.getConfig(somePublicNamespace);//configinstanceissingletonforeachnamespaceandisnevernull
StringsomeKey="someKeyFromPublicNamespace";
StringsomeDefaultValue="someDefaultValueForTheKey";
Stringvalue=config.getProperty(someKey,someDefaultValue);
3.2Spring整合方式
3.2.1配置
Apollo目前既支持比较传统的基于XML的配置,也支持目前比较流行的基于Java(推荐)的配置。
需要注意的是,如果之前有使用的,请替换成。
Spring3.1以后就不建议使用PropertyPlaceholderConfigurer了,要改用PropertySourcesPlaceholderConfigurer。
基于XML的配置
注:
需要把apollo相关的xmlnamespace加到配置文件头上,不然会报xml语法错误。
1.注入默认namespace的配置到Spring中
xmlversion="1.0"encoding="UTF-8"?
>
xmlns:
xsi=""
xmlns:
apollo=""
xsi:
schemaLocation="
">
--这个是最简单的配置形式,一般应用用这种形式就可以了,用来指示Apollo注入applicationnamespace的配置到Spring环境中-->
config/>
100}"/>
200}"/>
2.注入多个namespace的配置到Spring中
xmlversion="1.0"encoding="UTF-8"?
>
xmlns:
xsi=""
xmlns:
apollo=""
xsi:
schemaLocation="
">
--这个是最简单的配置形式,一般应用用这种形式就可以了,用来指示Apollo注入applicationnamespace的配置到Spring环境中-->
config/>
--这个是稍微复杂一些的配置形式,指示Apollo注入FX.apollo和FX.soanamespace的配置到Spring环境中-->
confignamespaces="FX.apollo,FX.soa"/>
100}"/>
200}"/>
3.注入多个namespace,并且指定顺序
Spring的配置是有顺序的,如果多个propertysource都有同一个key,那么最终是顺序在前的配置生效。
apollo:
config如果不指定order,那么默认是最低优先级。
xmlversion="1.0"encoding="UTF-8"?
>
xmlns:
xsi=""
xmlns:
apollo=""
xsi:
schemaLocation="
">
configorder="2"/>
--这个是最复杂的配置形式,指示Apollo注入FX.apollo和FX.soanamespace的配置到Spring环境中,并且顺序在application前面-->
confignamespaces="FX.apollo,FX.soa"order="1"/>
100}"/>
200}"/>
基于Java的配置(推荐)
相对于基于XML的配置,基于Java的配置是目前比较流行的方式,也是Spri