这里主要阐述的是,使用Apollo在本地开发阶段的过程,主要以SpringBoot项目为主。
目录
一、首先,本地可搭建一套简易的Apollo
二、依赖客户端,这里以maven为例
三、初始化Apollo中项目配置(appId、namespace等)
四、项目代码配置文件
五、代码中读取配置
六、测试
七、注意
Apollo作者提供了一个可供快速上手部署的demo。具体操作参照下方链接内容一步步进行即可:Quick-Start
然后进入代码开发。
Apollo的客户端jar包已经上传到中央仓库,应用在实际使用时只需要按照如下方式引入即可。
com.ctrip.framework.apollo
apollo-client
1.4.0
创建项目
创建后,默认会创建“application” namespace,我们可以进行一些SpringBoot项目的初始化配置,如server.port等。
项目使用Apollo有一些必要配置,Apollo客户端依赖于AppId,Apollo Meta Server等环境信息来工作。还有一些可选配置等,同时对于SpringBoot项目特殊的配置方式。
1、appId
这里推荐SpringBoot项目使用“app.properties”配置文件的方式配置appId。具体如下:
确保classpath:/META-INF/app.properties文件存在,并且其中内容形如
app.id=YOUR-APP-ID
文件位置如下
2、meta server
Apollo支持应用在不同的环境有不同的配置,所以需要在运行提供给Apollo客户端当前环境的Apollo Meta Server信息。默认情况下,meta server和config service是部署在同一个JVM进程,所以meta server的地址就是config service的地址。
为了实现meta server的高可用,推荐通过SLB(Software Load Balancer)做动态负载均衡。Meta server地址也可以填入IP,如http://1.1.1.1:8080,http://2.2.2.2:8080,不过生产环境还是建议使用域名(走slb),因为机器扩容、缩容等都可能导致IP列表的变化。
因为我们这里部署的是Quick-start apollo,所以meta server的地址就是config service的地址,直接指定config-server即可。
推荐这样配置:在Java程序启动脚本中,可以指定-Dapollo.meta=http://config-service-url。
如本地IDEA开发时,在启动Application Main时,配置,如下图
3、Environment
这里再配置下可选配置,因为quick-start apollo 默认了环境只有dev,需要客户端连接读取配置时指定。
同样本地开发推荐在Java程序启动脚本中,可以指定-Denv=YOUR-ENVIRONMENT,如上图中的VM options中指定了“-Dev=dev”。
4、最后就是配置项目resources下的“application.yml”或者“application.properties”
SpringBoot除了支持Spring XML集成方式以及Java代码集成方式以外,还支持通过application.properties/bootstrap.properties来配置,该方式能使配置在更早的阶段注入,比如使用@ConditionalOnProperty的场景或者是有一些spring-boot-starter在启动阶段就需要读取配置做一些事情(如dubbo-spring-boot-project),同时因为spring-boot-starter做了很多起步依赖初始化,不同于以往的SSM项目,我们在xml中配置datasource,容器bean配置等直接初始化指定bean。而SpringBoot只需要直接写配置值就行。所以对于Spring Boot环境建议通过以下方式来接入Apollo(需要0.10.0及以上版本)。使用方式很简单,只需要在application.properties/bootstrap.properties中按照如下样例配置即可。
1)、注入默认application namespace的配置示例
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
2)、注入非默认application namespace或多个namespace的配置示例
apollo.bootstrap.enabled = true
# will inject 'application', 'FX.apollo' and 'application.yml' namespaces in bootstrap phase
apollo.bootstrap.namespaces = application,FX.apollo,application.yml
这里我直接将配置放在了“application-dev.yml”中
除了容器启动时会从apollo中读取配置,我们还将经常会在代码通过诸如@Value或者@ConfigurationProperties去读取配置。
所以着重说这两个,以@Value为例,与日常使用无任何差异。不管你使用@Component还是@Configuration均可实时更新 只是两个注解本身的区别。同样配置读取也是Spring的规则,多个一样的property key,只加载第一个。对于apollo配置来说,“apollo.bootstrap.namespaces”配置的namespace,哪个namespace在前,就先读取哪个。假设datasource namespace下有key为“name”的配置了,application namespace下也有key为“name”的配置,按上图的中namespace顺序,则只加载application中的,不会再加载datasource中的“name”了。同理如果datasource中没有,则读取application中的。
对于@ConfigurationProperties注解比较特殊,因为apollo没办法配置改变时去实时刷新该bean,这也是由于该注解本身的特性,所以需要进行额外的处理。
@ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEvent或RefreshScope。相关代码实现,可以参考apollo-use-cases项目中的ZuulPropertiesRefresher.java和apollo-demo项目中的SampleRedisConfig.java以及SpringBootApolloRefreshConfig.java。
代码:将@RefreshScope注解标记在@ConfigurationProperties修饰的类上,同时编写apollo监听器,如果监听到该类的配置发生变化,则手动刷新该bean。
@ConfigurationProperties(prefix = "nwpu")
@Component("nwpuDTO")
@RefreshScope
public class NwpuDTO {
/**
* 学校名
*/
private String name;
/**
* 联系电话
*/
private String url;
/**
* 邮编
*/
private int postCode;
/**
* 学校性质
*/
private String[] properties;
/**
* 学校地点
*/
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPostCode() {
return postCode;
}
public void setPostCode(int postCode) {
this.postCode = postCode;
}
public String[] getProperties() {
return properties;
}
public void setProperties(String[] properties) {
this.properties = properties;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
@Configuration
public class ApolloConfig {
@Autowired
private RefreshScope refreshScope;
@ApolloConfigChangeListener(value = "application", interestedKeyPrefixes = "nwpu")
private void onChangeToApplication(ConfigChangeEvent changeEvent) {
// 如果@ConfigurationProperties配置发生变化,则需要手动刷新
refreshScope.refresh("nwpuDTO");
}
}
监听器的内容可参考这个:Apollo 监听器ConfigListener
可写接口打印出读取的配置的实例
对于SpringBoot项目,如果指定了apollo.bootstrap.namespaces,哪个namespace在前就先加载。
相当于已经注入环境为apollo,并且namespace开启,所以根据配置文件生效顺序可知,如果apollo中有配置则apollo,只会成功加载一次,如果apollo中没有,则加载本地配置。
如果apollo.bootstrap.namespaces只配置了datasource,那么只有dataSource生效。如果再某个类上标记@EnableApolloConfig,无论在何处标记都表示又开启一个或多个namespace下的配置,默认为application。
那么会先读取datasource下的配置,然后application,再本地。可以理解为apollo.bootstrap.namespaces会注册environment(有且仅有一个,范围为datasource),然后@EnableApolloConfig又会将该environment范围变成
datasource和application,然后配置是从environment中取得。 (源码待研究,内部加载机制)
记住:有且仅有一个environment
配置读取顺序:命令行参数 > java system变量 -D > Apollo > 本地配置文件