Apollo配置中心_第1页
Apollo配置中心_第2页
Apollo配置中心_第3页
Apollo配置中心_第4页
Apollo配置中心_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Apollo配置中心资料来源:/ctripcorp/apollo更多资料请参考:/lepdou/apollo设计一 总体设计1 基础模型2 架构模块3 各模块概要介绍1 Config Service 提供配置获取接口 提供配置更新推送接口(基于Http long polling) o 服务端使用Spring DeferredResult实现异步化,从而大大增加长连接数量o 目前使用的tomcat embed默认配置是最多10000个连接(可以调整),使用了4C8G的虚拟机实测可以支撑10000个连接,所以满足需求(一个应用实例只会发起一个长连接)。 接口服务对象为Apollo客户端2 Admin Service 提供配置管理接口 提供配置修改、发布等接口 接口服务对象为Portal3 Meta Server Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port) Client通过域名访问Meta Server获取Config Service服务列表(IP+Port) Meta Server从Eureka获取Config Service和Admin Service的服务信息,相当于是一个Eureka Client 增设一个Meta Server的角色主要是为了封装服务发现的细节,对Portal和Client而言,永远通过一个Http接口获取Admin Service和Config Service的服务信息,而不需要关心背后实际的服务注册和发现组件 Meta Server只是一个逻辑角色,在部署时和Config Service是在一个JVM进程中的4 Eureka 基于Eureka和Spring Cloud Netflix提供服务注册和发现 Config Service和Admin Service会向Eureka注册服务,并保持心跳 为了简单起见,目前Eureka在部署时和Config Service是在一个JVM进程中的(通过Spring Cloud Netflix)5 Portal 提供Web界面供用户管理配置 通过Meta Server获取Admin Service服务列表(IP+Port),通过IP+Port访问服务 在Portal侧做load balance、错误重试6 Client Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能 通过Meta Server获取Config Service服务列表(IP+Port),通过IP+Port访问服务 在Client侧做load balance、错误重试二、服务端设计1 配置发布后的实时推送设计在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。下面我们简要看一下这块是怎么设计实现的。上图简要描述了配置发布的大致过程:1. 用户在Portal操作配置发布2. Portal调用Admin Service的接口操作发布3. Admin Service发布配置后,发送ReleaseMessage给各个Config Service4. Config Service收到ReleaseMessage后,通知对应的客户端2 发送ReleaseMessage的实现方式Admin Service在配置发布后,需要通知所有的Config Service有配置发布,从而Config Service可以通知对应的客户端来拉取最新的配置。从概念上来看,这是一个典型的消息使用场景,Admin Service作为producer发出消息,各个Config Service作为consumer消费消息。通过一个消息组件(Message Queue)就能很好的实现Admin Service和Config Service的解耦。在实现上,考虑到Apollo的实际使用场景,以及为了尽可能减少外部依赖,我们没有采用外部的消息中间件,而是通过数据库实现了一个简单的消息队列。实现方式如下:1. Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace,参见DatabaseMessageSender 2. Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见ReleaseMessageScanner 3. Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2,消息监听器的注册过程参见ConfigServiceAutoConfiguration 4. NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端示意图如下:2.1.2 Config Service通知客户端的实现方式上一节中简要描述了NotificationControllerV2是如何得知有配置发布的,那NotificationControllerV2在得知有配置发布后是如何通知到客户端的呢?实现方式如下:1. 客户端会发起一个Http请求到Config Service的notifications/v2接口,也就是NotificationControllerV2,参见RemoteConfigLongPollService 2. NotificationControllerV2不会立即返回结果,而是通过Spring DeferredResult把请求挂起3. 如果在30秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端4. 如果有该客户端关心的配置发布,NotificationControllerV2会调用DeferredResult的setResult方法,传入有配置变化的namespace信息,同时该请求会立即返回。客户端从返回的结果中获取到配置变化的namespace后,会立即请求Config Service获取该namespace的最新配置。三、客户端设计上图简要描述了Apollo客户端的实现原理:1. 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)2. 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。 o 这是一个fallback机制,为了防止推送机制失效导致配置不更新o 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modifiedo 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。3. 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中4. 客户端会把从服务端获取到的配置在本地文件系统缓存一份 o 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置5. 应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知Java客户端应用一、准备工作1.1 环境要求 Java: 1.7+ Guava: 15.0+ o Apollo客户端默认会引用Guava 19,如果你的项目引用了其它版本,请确保版本号大于等于15.01.2 必选设置Apollo客户端依赖于AppId,Environment等环境信息来工作 1.2.1 AppIdAppId是应用的身份信息,是从服务端获取配置的一个重要信息。请确保classpath:/META-INF/perties文件存在,并且其中内容形如:app.id=YOUR-APP-ID文件位置参考如下:也可通过System Property传入app.id信息,如-Dapp.id=YOUR-APP-ID注:app.id是用来标识应用身份的唯一id,格式为string。1.2.2 EnvironmentApollo支持应用在不同的环境有不同的配置,所以Environment是另一个从服务器获取配置的重要信息。Environment可以通过以下3种方式的任意一个配置:1. 通过Java System Propertyo 可以通过Java的System Property env来指定环境o 在Java程序启动脚本中,可以指定-Denv=YOUR-ENVIRONMENT 如果是运行jar文件,需要注意格式是java -Denv=YOUR-ENVIRONMENT -jar xxx.jar o 注意key为全小写2. 通过操作系统的System Environmento 还可以通过操作系统的System Environment ENV来指定o 注意key为全大写3. 通过配置文件o 最后一个推荐的方式是通过配置文件来指定env=YOUR-ENVIRONMENT o 对于Mac/Linux,文件位置为/opt/settings/perties o 对于Windows,文件位置为C:perties 文件内容形如:env=DEV目前,env支持以下几个值(大小写不敏感) DEV FAT UAT(目前不设UAT环境) PRO注:运行时会通过env信息匹配meta server信息,比如env是DEV的话,就会读取apollo-core.jar中perties中的dev_meta信息。如果希望在运行时覆盖meta server信息,有两个方法:1. 通过-Denv_meta=xxx来覆盖,如-Ddev_meta=http:/someIp:8080 2. 准备一份单独的perties,放在程序运行的classpath中,或者放在程序运行目录下,具体可以参考perties的加载逻辑 - ResourceUtils和perties原始文件,需要注意的是在perties中需要用形如env.meta=xxx来覆盖,如dev.meta=http:/someIp:8080。 1.2.3 本地缓存路径Apollo客户端会把从服务端获取到的配置在本地文件系统缓存一份,用于在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置,不影响应用正常运行。本地缓存路径位于以下路径,所以请确保/opt/data或C:optdata目录存在,且应用有读写权限。 Mac/Linux: /opt/data/appId/config-cache Windows: C:optdataappIdconfig-cache本地配置文件会以下面的文件名格式放置于本地缓存路径下:appId+cluster+perties appId就是应用自己的appId,如100004458 cluster就是应用使用的集群,一般在本地模式下没有做过配置的话,就是default namespace就是应用使用的配置namespace,一般是application 文件内容以properties格式存储,比如如果有两个key,一个是request.timeout,另一个是batch,那么文件内容就是如下格式:request.timeout=2000batch=2000注:本地缓存路径也可用于容灾目录,如果应用在所有config service都挂掉的情况下需要扩容,那么也可以先把配置从已有机器上的缓存路径复制到新机器上的相同缓存路径1.2.4 可选设置Cluster(集群)Apollo支持配置按照集群划分,也就是说对于一个appId和一个环境,对不同的集群可以有不同的配置。如果需要使用这个功能,你可以通过以下方式来指定运行时的集群:1. 通过Java System Propertyo 我们可以通过Java的System Property设置apollo.cluster来指定运行时集群(注意key为全小写)o 例如,可以在程序启动时通过-Dapollo.cluster=SomeCluster来指定运行时的集群为SomeCluster2. 通过配置文件o 首先确保/opt/settings/perties(Mac/Linux)或C:perties(Windows)在目标机器上存在o 在这个文件中,可以设置数据中心集群,如idc=xxx o 注意key为全小写Cluster Precedence(集群顺序)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)加载配置二、Maven Dependency由于客户端jar包中会包含meta server信息,无法上传一个统一的jar包到中央仓库,所以请按照分布式部署指南的文档说明打包并上传到自己公司的Maven私服。应用在实际使用时只需要按照如下方式引入即可。 com.ctrip.framework.apollo apollo-client 0.9.1 三、客户端用法Apollo支持API方式和Spring整合方式,该怎么选择用哪一种方式? API方式灵活,功能完备,配置值实时更新(热发布),支持所有Java环境。 Spring方式接入简单,结合Spring有N种酷炫的玩法,如 o Placeholder方式: 代码中使用,如:Value($someKeyFromApollo:someDefaultValue) perties中使用,如:spring.datasource.url: $someKeyFromApollo:someDefaultValue o Spring boot的ConfigurationProperties方式o 缺点是这类注入的值在变化后不会重新注入,需要重启才会更新,如果需要配置值实时更新,可以参考后续3.2.2 Spring Placeholder的使用的说明 Spring方式也可以结合API方式使用,如注入Apollo的Config对象,就可以照常通过API方式获取配置了: ApolloConfigprivate Config config; /inject config for namespace application3.1 API使用方式API方式是最简单、高效使用Apollo配置的方式,不依赖Spring框架即可使用。3.1.1 获取默认namespace的配置(application)Config config = ConfigService.getAppConfig(); /config instance is singleton for each namespace and is never nullString someKey = someKeyFromDefaultNamespace;String someDefaultValue = someDefaultValueForTheKey;String value = config.getProperty(someKey, someDefaultValue);通过上述的config.getProperty可以获取到someKey对应的实时最新的配置值。另外,配置值从内存中获取,所以不需要应用自己做缓存。3.1.2 监听配置变化事件监听配置变化事件只在应用真的关心配置变化,需要在配置变化时得到通知时使用,比如:数据库连接串变化后需要重建连接等。如果只是希望每次都取到最新的配置的话,只需要按照上面的例子,调用config.getProperty即可。Config config = ConfigService.getAppConfig(); /config instance is singleton for each namespace and is never nullconfig.addChangeListener(new ConfigChangeListener() Override public void onChange(ConfigChangeEvent changeEvent) System.out.println(Changes for namespace + changeEvent.getNamespace(); for (String key : changeEvent.changedKeys() ConfigChange change = changeEvent.getChange(key); System.out.println(String.format(Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s, change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType(); );3.1.3 获取公共Namespace的配置String somePublicNamespace = CAT;Config config = ConfigService.getConfig(somePublicNamespace); /config instance is singleton for each namespace and is never nullString someKey = someKeyFromPublicNamespace;String someDefaultValue = someDefaultValueForTheKey;String value = config.getProperty(someKey, someDefaultValue);3.2 Spring整合方式3.2.1 配置Apollo也支持和Spring整合(Spring 3.1.1+),只需要做一些简单的配置就可以了。Apollo目前既支持比较传统的基于XML的配置,也支持目前比较流行的基于Java(推荐)的配置。需要注意的是,如果之前有使用org.springframework.beans.factory.config.PropertyPlaceholderConfigurer的,请替换成org.springframework.context.support.PropertySourcesPlaceholderConfigurer。Spring 3.1以后就不建议使用PropertyPlaceholderConfigurer了,要改用PropertySourcesPlaceholderConfigurer。 基于XML的配置注:需要把apollo相关的xml namespace加到配置文件头上,不然会报xml语法错误。1.注入默认namespace的配置到Spring中 2.注入多个namespace的配置到Spring中 3.注入多个namespace,并且指定顺序Spring的配置是有顺序的,如果多个property source都有同一个key,那么最终是顺序在前的配置生效。apollo:config如果不指定order,那么默认是最低优先级。 基于Java的配置(推荐)相对于基于XML的配置,基于Java的配置是目前比较流行的方式,也是Spring Boot的默认配置方式。注意EnableApolloConfig要和Configuration一起使用,不然不会生效。1.注入默认namespace的配置到Spring中/这个是最简单的配置形式,一般应用用这种形式就可以了,用来指示Apollo注入application namespace的配置到Spring环境中ConfigurationEnableApolloConfigpublic class AppConfig Bean public TestJavaConfigBean javaConfigBean() return new TestJavaConfigBean(); 2.注入多个namespace的配置到Spring中ConfigurationEnableApolloConfigpublic class SomeAppConfig Bean public TestJavaConfigBean javaConfigBean() return new TestJavaConfigBean(); /这个是稍微复杂一些的配置形式,指示Apollo注入FX.apollo和FX.soa namespace的配置到Spring环境中ConfigurationEnableApolloConfig(FX.apollo, FX.soa)public class AnotherAppConfig 3.注入多个namespace,并且指定顺序/这个是最复杂的配置形式,指示Apollo注入FX.apollo和FX.soa namespace的配置到Spring环境中,并且顺序在application前面ConfigurationEnableApolloConfig(order = 2)public class SomeAppConfig Bean public TestJavaConfigBean javaConfigBean() return new TestJavaConfigBean(); ConfigurationEnableApolloConfig(value = FX.apollo, FX.soa, order = 1)public class AnotherAppConfig 3.2.2 Spring Placeholder的使用Spring应用通常会使用Placeholder来注入配置,使用的格式形如$someKey:someDefaultValue,如$timeout:100。冒号前面的是key,冒号后面的是默认值。建议在实际使用时尽量给出默认值,以免由于key没有定义导致运行时错误。出于安全性考虑,目前placeholder只会在启动的时候赋值,在运行过程中即便在Apollo上修改发布了配置,placeholder的值也不会更新。如果需要运行时动态更新placeholder的值,有以下几种方式:1. 程序监听Apollo的ConfigChangeListener(如ApolloConfigChangeListener),然后通过自己的代码来更新placeholder的值。2. 不使用placeholder方式获取配置,而是直接从config对象中获取配置。样例代码可以参考3.2.3 Spring Annotation支持的getBatch和getTimeout方法。3. 【推荐】使用Spring Cloud的RefreshScope o 在需要刷新配置的bean上加RefreshScopeo 加一个Apollo的ConfigChangeListener,然后在配置变化时调用RefreshScope的refreshAll()或者refresh(String name)的方法。o 如果要刷新的不是自己的bean,而是spring cloud的bean,如spring cloud zuul,那么可以调用org.springframework.cloud.context.refresh.ContextRefresher#refresh来刷新配置o 相关代码实现,可以参考apollo-demo项目中的AnnotatedBean.java和ApolloRefreshConfig.java o 在非Spring Boot环境使用RefreshScope需要引入RefreshAutoConfiguration,引入方式可以参考apollo-demo项目中的相关配置代码:JavaConfig示例,XmlConfig示例 XML使用方式假设我有一个TestXmlBean,它有两个配置项需要注入:public class TestXmlBean private int timeout; private int batch; public void setTimeout(int timeout) this.timeout = timeout; public void setBatch(int batch) this.batch = batch; public int getTimeout() return timeout; public int getBatch() return batch; 那么,我在XML中会使用如下方式来定义(假设应用默认的application namespace中有timeout和batch的配置项): Java Config使用方式假设我有一个TestJavaConfigBean,通过Java Config的方式还可以使用Value的方式注入:public class TestJavaConfigBean Value($timeout:100) private int timeout; private int batch; Value($batch:200) public void setBatch(int batch) this.batch = batch; public int getTimeout() return timeout; public int getBatch() return batch; 在Configuration类中按照下面的方式使用(假设应用默认的application namespace中有timeout和batch的配置项):ConfigurationEnableApolloConfigpublic class AppConfig Bean public TestJavaConfigBean javaConfigBean() return new TestJavaConfigBean(); ConfigurationProperties使用方式Spring Boot提供了ConfigurationProperties把配置注入到bean对象中。Apollo也支持这种方式,下面的例子会把redis.cache.expireSeconds和mandTimeout分别注入到SampleRedisConfig的expireSeconds和commandTimeout字段中。ConfigurationProperties(prefix = redis.cache)public class SampleRedisConfig private int expireSeconds; private int commandTimeout; public void setExpireSeconds(int expireSeconds) this.expireSeconds = expireSeconds; public void setCommandTimeout(int commandTimeout) mandTimeout = commandTimeout; 在Configuration类中按照下面的方式使用(假设应用默认的application namespace中有redis.cache.expireSeconds和mandTimeout的配置项):ConfigurationEnableApolloConfigpublic class AppConfig Bean public SampleRedisConfig sampleRedisConfig() return new SampleRedisConfig(); 3.2.3 Spring Annotation支持Apollo同时还增加了两个新的Annotation来简化在Spring环境中的使用。1. ApolloConfig o 用来自动注入Config对象2. ApolloConfigChangeListener o 用来自动注册ConfigChangeListener使用样例如下:public class TestApolloAnnotationBean ApolloConfig private Config config; /inject config for namespace application ApolloConfig(application) private Config anotherConfig; /inject config for namespace application ApolloConfig(FX.apollo) private Config yetAnotherConfig; /inject config for namespace FX.apollo Value($batch:100) private int batch; /config change listener for namespace application ApolloConfigChangeListener private void someOnChange(ConfigChangeEvent changeEvent) /update injected value of batch if it is changed in Apollo if (changeEvent.isChanged(batch) batch = config.getIntProperty(batch, 100); /config change listener for namespace application ApolloConfigChangeListener(application) private void anotherOnChange(ConfigChangeEvent changeEvent) /do something /config change listener for namespaces application and FX.apollo ApolloConfigChangeListener(application, FX.apollo) private void yetAnotherOnChange(ConfigChangeEvent changeEvent) /do something /example of getting config from Apollo directly /this will always return the latest value of timeout public int getTimeout() return config.getIntProperty(timeout, 200); /example of getting config from injected value /the program needs to update the injected value when batch is changed in Apollo using ApolloConfigChangeListener shown above public int getBatch() return this.batch; 在Configuration类中按照下面的方式使用:ConfigurationEnableApolloConfigpublic class AppConfig Bean public TestApolloAnnotationBean testApolloAnnotationBean() return new TestApolloAnnotationBean(); 3.2.4 已有配置迁

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论