Apollo客户端使用与配置解析
前言:
本文主要是分享Apollo Client客户端使用过程中,遇到的问题、解决问题及分析代码逻辑的过程。其中一个重要问题就是关于apollo.bootstrap.enabled = true的使用及注意事项。
一、准备工作
1.1 环境要求
本文是基于Apollo v1.1.1版本,springboot项目客户端引入的是:
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-core</artifactId>
<version>1.1.0</version>
</dependency>
1.2 必选设置
Apollo客户端依赖于AppId(项目ID),Apollo Meta Server(配置中心Eureka地址)
1.2.1 AppId
项目application.properties文件内容:
app.id=demo-test
apollo.bootstrap.enabled = true
注:app.id是用来标识应用身份的唯一id,格式为string。
apollo.bootstrap.enabled官方解释为注入默认application namespace的配置示例
1.2.2 Apollo Meta Server
apollo.meta(配置中心Eureka地址) 配置如下:
Apollo默认会读取系统上/opt/settings/server.properties(linux)或
C:\ opt \settings\server.properties(windows)文件(手动新建目录与文件)
apollo.meta=http://localhost:8089
1.3 配置中心添加配置
1.4 springboot项目客户端代码
启动类:
package com.test.apollodemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import com.test.apollodemo.configbean.MysqlConfig2;
import com.test.apollodemo.configbean.MysqlConfigBean;
@Component
@ComponentScan("com.test")
@SpringBootApplication
public class SpringBootConsoleApplication implements CommandLineRunner {
@Autowired
MysqlConfig2 mysqlConfig2;
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
while(true) {
System.out.println("+++++++++++++++++++++++++");
System.out.println(mysqlConfig2.getMysqlConfigBean().getUrl());
System.out.println(mysqlConfig2.getMysqlConfigBean().getDes());
Thread.sleep(3000);
}
}
}
实体类:
package com.test.apollodemo.configbean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
@Configuration
@EnableApolloConfig("mysqlcfg")
public class MysqlConfig2 {
@Bean
public MysqlConfigBean getMysqlConfigBean() {
return new MysqlConfigBean();
}
public class MysqlConfigBean{
@Value("${url:}")
private String url;
@Value("${username:}")
private String username;
@Value("${password:}")
private String password;
@Value("${des:我是默认值}")
private String des;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/**
* @ApolloConfigChangeListener用来自动注册ConfigChangeListener
*/
@ApolloConfigChangeListener("mysqlcfg")
private void someOnChange(ConfigChangeEvent changeEvent) {
for(String changeKey:changeEvent.changedKeys()) {
ConfigChange change = changeEvent.getChange(changeKey);
System.out.println(String.format("%%%%%%%%%%Found datasource change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()));
}
}
}
1.5 springboot项目执行结果
1.6 java项目案例
1.6.1 代码案例
package com.demo.apollo;
import java.io.IOException;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
public class Apollo {
public static void main(String[] args) throws InterruptedException, IOException {
//src目录下新建:META-INF\app.properties,
//app.id配置如下:app.id=demo-test3//项目ID(唯一)
Config config = ConfigService.getConfig("mysqlcfg");
String someKey = "url";
String someDefaultValue = "我是默认值";
String value = config.getProperty(someKey, someDefaultValue);
System.out.println(value);
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
// TODO Auto-generated method stub
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()));
}
}
});
while(true) {
System.out.println(config.getProperty(someKey, someDefaultValue));
Thread.sleep(3000);
}
}
}
1.6.2 依赖引入
Apollo客户端核心jar包:
apollo-client-1.1.0.jar
apollo-core-1.1.0.jar
只导入核心Jar包报错如下:
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class com.ctrip.framework.apollo.tracer.Tracer
at com.ctrip.framework.apollo.build.ApolloInjector.getInstance(ApolloInjector.java:37)
at com.ctrip.framework.apollo.ConfigService.getManager(ConfigService.java:25)
at com.ctrip.framework.apollo.ConfigService.getConfig(ConfigService.java:61)
核心jar包依赖包:
aopalliance-1.0.jar
gson-2.8.5.jar
guava-26.0-jre.jar
guice-4.2.1.jar
javax.inject-1.jar
log4j-api-2.10.0.jar
log4j-core-2.10.0.jar
log4j-slf4j-impl-2.10.0.jar
slf4j-api-1.7.25.jar
1.6.3 日志
引入log4j2.xml日志文件:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<properties>
<!-- 文件输出格式 -->
<property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%T-%thread] %c [%L] -| %msg%n</property>
</properties>
<appenders>
<Console name="CONSOLE" target="system_out">
<PatternLayout pattern="${PATTERN}" />
</Console>
</appenders>
<loggers>
<logger name="com.apollo.demo" level="debug" />
<root level="warn">
<appenderref ref="CONSOLE" />
</root>
</loggers>
</configuration>
二、引入问题
本来认为执行结果是:
+++++++++++++++++++++++++++++++++++++++++
localhost
application-des
为什么会出现这个情况?(注意:mysqlcfg不管是关联还是私用,des的值本来应该为mysqlcfg-des)
下面将分析出现这个结果的原因及解决办法。
三、代码分析
3.1 apollo-client源码分析
3.3.1 第一步-切入点
首先分析,客户端肯定从服务端,获取了application 和mysqlcfg的配置项,所以从 客户端请求url组装进行分析:
类RemoteConfigLongPollService:
String assembleLongPollRefreshUrl(String uri, String appId, String cluster, String dataCenter, Map<String, Long> notificationsMap) {
Map<String, String> queryParams = Maps.newHashMap();
queryParams.put("appId", queryParamEscaper.escape(appId));
queryParams.put("cluster", queryParamEscaper.escape(cluster));
//下面这段就是告诉服务器,我要获取application和mysqlcfg的配置项
queryParams
.put("notifications",queryParamEscaper.escape(assembleNotifications(notificationsMap)));
if (!Strings.isNullOrEmpty(dataCenter)) {
queryParams.put("dataCenter", queryParamEscaper.escape(dataCenter));
}
String localIp = m_configUtil.getLocalIp();
if (!Strings.isNullOrEmpty(localIp)) {
queryParams.put("ip", queryParamEscaper.escape(localIp));
}
String params = MAP_JOINER.join(queryParams);
if (!uri.endsWith("/")) {
uri += "/";
}
return uri + "notifications/v2?" + params;
}
方法doLongPollingRefresh会调用:
url =assembleLongPollRefreshUrl(lastServiceDto.getHomepageUrl(), appId, cluster, dataCenter, m_notifications);
doLongPollingRefresh到startLongPolling到submit
3.3.2 第二步-反向追溯
1.类 RemoteConfigRepository:
private void scheduleLongPollingRefresh() {
remoteConfigLongPollService.submit(m_namespace, this);
}
然后:
public RemoteConfigRepository(String namespace) {
m_namespace = namespace;
m_configCache = new AtomicReference<>();
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
m_longPollServiceDto = new AtomicReference<>();
m_remoteMessages = new AtomicReference<>();
m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
m_configNeedForceRefresh = new AtomicBoolean(true);
m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
m_configUtil.getOnErrorRetryInterval() * 8);
gson = new Gson();
this.trySync();
this.schedulePeriodicRefresh();
this.scheduleLongPollingRefresh();
}
2.继续追溯类DefaultConfigFactory:
LocalFileConfigRepository createLocalConfigRepository(String namespace) {
if (m_configUtil.isInLocalMode()) {
logger.warn(
"==== Apollo is in local mode! Won't pull configs from remote server for namespace {} ! ====",
namespace);
return new LocalFileConfigRepository(namespace);
}
return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
}
RemoteConfigRepository createRemoteConfigRepository(String namespace) {
return new RemoteConfigRepository(namespace);
}
然后到:
@Override
public Config create(String namespace) {
DefaultConfig defaultConfig =
new DefaultConfig(namespace, createLocalConfigRepository(namespace));
return defaultConfig;
}
3. 类DefaultConfigManager:
@Override
public Config getConfig(String namespace) {
Config config = m_configs.get(namespace);
if (config == null) {
synchronized (this) {
config = m_configs.get(namespace);
if (config == null) {
ConfigFactory factory = m_factoryManager.getFactory(namespace);
config = factory.create(namespace);
m_configs.put(namespace, config);
}
}
}
return config;
}
4. 类ConfigService:
public static Config getConfig(String namespace) {
return s_instance.getManager().getConfig(namespace);
}
5. 类ApolloApplicationContextInitializer:
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
initializeSystemProperty(environment);
String enabled = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "false");
if (!Boolean.valueOf(enabled)) {
logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
return;
}
logger.debug("Apollo bootstrap config is enabled for context {}", context);
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//already initialized
return;
}
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
logger.debug("Apollo bootstrap namespaces: {}", namespaces);
List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);
CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
for (String namespace : namespaceList) {
Config config = ConfigService.getConfig(namespace);
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
environment.getPropertySources().addFirst(composite);
}
6. 接口PropertySourcesConstants:
package com.ctrip.framework.apollo.spring.config;
public interface PropertySourcesConstants {
String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources";
String APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME = "ApolloBootstrapPropertySources";
String APOLLO_BOOTSTRAP_ENABLED = "apollo.bootstrap.enabled";
String APOLLO_BOOTSTRAP_NAMESPACES = "apollo.bootstrap.namespaces";
}
最终找出原因是跟 APOLLO_BOOTSTRAP_NAMESPACES有关
3.2 总结
3.2.1 问题解决
最终找出原因:如果配置文件没有配置apollo.bootstrap.namespaces时,系统默认namespaces为application,所以客户端会去请求application配置项。
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
所以在application.properties文件中添加:
apollo.bootstrap.namespaces = mysqlcfg,保证客户端不会去获取application配置项,这样结果输出正常。
3.2.2逻辑流程图
四、其他
关于apollo.meta参数配置优先级
(1) JVM system property 'apollo.meta',
(2) OS env variable 'APOLLO_META'
(3) property 'apollo.meta' from server.properties
(4) property 'apollo.meta' from app.properties
一般项目常用(3)的方式,配置apollo.meta。
本文纯属个人观点
转载于:https://www.cnblogs.com/haonan-fabric/p/10131742.html
Apollo客户端使用与配置解析相关推荐
- Apollo客户端配置获取深度解析
Apollo客户端配置获取深度解析 Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理 ...
- Apollo使用篇 - Apollo客户端的使用
准备工作 环境要求 Java 1.8+ Guava 20.0+ 必选设置 AppId AppId 是应用的身份信息,是从服务端获取配置的一个重要信息. 有如下几种方式设置,按照优先级从高到低分别为: ...
- Dubbo 框架设计与源码解读(配置解析优先级、线程分配、负载均衡、容错方案)
整体框架设计 图例说明: 图中左边淡蓝背景的为服务消费⽅使⽤的接⼝,右边淡绿⾊背景的为服务提供⽅使⽤的接⼝,位于中轴线上的为双⽅都⽤到的接⼝. 图中从下⾄上分为⼗层,各层均为单向依赖,右边的⿊⾊箭头代 ...
- java的Apollo的功能_Apollo配置中心
1什么是配置中心? 传统单体应用存在一些潜在缺陷,如随着规模的扩大,部署效率降低,团队协作效率差,系统可靠性变差,维护困难,新功能上线周期长等,所以迫切需要一种新的架构去解决这些问题,而微服务( mi ...
- 运维之DNS服务器Bind9配置解析和基础示例及附带命令
0x03 Bind 配置解析 实例1.DNS主从区域传输介绍与配置 实例2.DNS区域传输限制 实例3.DNS部分二级域名解析 示例1.采用Bind建立一个A记录DNS服务器 示例2.采用Bind建立 ...
- 三、nginx服务的nginx.conf的参数配置解析
前一篇:二.nginx服务的nginx.conf配置参数解析 后一篇:四.nginx服务器的参数配置解析 目录 一.虚拟主机设定模块 1.upstream模块配置样式 1.1.默认配置 1.2.wei ...
- 获取并显示服务器数据,客户端获取服务器数据解析
客户端获取服务器数据解析 内容精选 换一换 VR云渲游平台提供了设备的实时监控功能,您可以通过监控大屏,查看指定设备在云上运行时的实时监控数据.当设备处于"运行中"状态时,才可以查 ...
- Apache入门 篇(二)之apache 2.2.x常用配置解析
一.httpd 2.2.x目录结构 Cnetos 6.10 YUM安装httpd 2.2.x # yum install -y httpd 程序环境主配置文件:/etc/httpd/conf/http ...
- Apollo 客户端日志抛异常 c.c.f.a.i.RemoteConfigLongPollService : Long polling failed, will retry ...
目录 问题背景 解决办法 根因分析 问题背景 微服务依赖 apollo-client 并完成配置.启动微服务之后,会每隔两分钟在控制台输出内容 " c.c.f.a.i.RemoteConfi ...
- nginx配置解析流程
上一篇文章分析了nginx配置文件缓冲区的管理,接下来将详细分析nginx是如何解析配置文件的.包含模块上下文结构的创建.core核心模块的解析.event事件模块的解析.http模块的解析. 一.模 ...
最新文章
- Shiro第一个程序:官方快速入门程序Qucickstart详解教程
- 实现一个队列,使得push_rear(), pop_front() 和get_min()的时间复杂度为O(1)
- 如何遍历JavaScript对象?
- 洛谷P1133 教主的花园 动态规划
- Android之MVVM框架 - 数据绑定
- eclipse查看jar包源代码
- PyQt5 QTableView 全部item居中
- java 多层异常_Java多层嵌套异常处理的基本流程
- CSS 全解析实战(一)-导读
- 查询雇佣的所有员工_想要最好的员工? 让他们自己雇用
- python实例变量命名规则_Java,Python的一些命名规范
- SSIS - 7.发邮件任务
- 敏捷开发免费管理工具——火星人预览(四)
- java实验报告实验环境_Java实验报告一:Java开发环境的熟悉
- 聚类的基本概念-聚类与分类的区别
- C语言课程设计报告-菜单设计
- 台风怎么看内存颗粒_使用300多元的D4 16G内存是种什么体验
- 详解TCP数据包中SYN,ACK字段与数据发送的关联
- Vue后台管理通用模板
- 经典论文阅读笔记——VIT、Swin Transformer、MAE、CILP