前言:

本文主要是分享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客户端使用与配置解析相关推荐

  1. Apollo客户端配置获取深度解析

    Apollo客户端配置获取深度解析 Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理 ...

  2. Apollo使用篇 - Apollo客户端的使用

    准备工作 环境要求 Java 1.8+ Guava 20.0+ 必选设置 AppId AppId 是应用的身份信息,是从服务端获取配置的一个重要信息. 有如下几种方式设置,按照优先级从高到低分别为: ...

  3. Dubbo 框架设计与源码解读(配置解析优先级、线程分配、负载均衡、容错方案)

    整体框架设计 图例说明: 图中左边淡蓝背景的为服务消费⽅使⽤的接⼝,右边淡绿⾊背景的为服务提供⽅使⽤的接⼝,位于中轴线上的为双⽅都⽤到的接⼝. 图中从下⾄上分为⼗层,各层均为单向依赖,右边的⿊⾊箭头代 ...

  4. java的Apollo的功能_Apollo配置中心

    1什么是配置中心? 传统单体应用存在一些潜在缺陷,如随着规模的扩大,部署效率降低,团队协作效率差,系统可靠性变差,维护困难,新功能上线周期长等,所以迫切需要一种新的架构去解决这些问题,而微服务( mi ...

  5. 运维之DNS服务器Bind9配置解析和基础示例及附带命令

    0x03 Bind 配置解析 实例1.DNS主从区域传输介绍与配置 实例2.DNS区域传输限制 实例3.DNS部分二级域名解析 示例1.采用Bind建立一个A记录DNS服务器 示例2.采用Bind建立 ...

  6. 三、nginx服务的nginx.conf的参数配置解析

    前一篇:二.nginx服务的nginx.conf配置参数解析 后一篇:四.nginx服务器的参数配置解析 目录 一.虚拟主机设定模块 1.upstream模块配置样式 1.1.默认配置 1.2.wei ...

  7. 获取并显示服务器数据,客户端获取服务器数据解析

    客户端获取服务器数据解析 内容精选 换一换 VR云渲游平台提供了设备的实时监控功能,您可以通过监控大屏,查看指定设备在云上运行时的实时监控数据.当设备处于"运行中"状态时,才可以查 ...

  8. 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 ...

  9. Apollo 客户端日志抛异常 c.c.f.a.i.RemoteConfigLongPollService : Long polling failed, will retry ...

    目录 问题背景 解决办法 根因分析 问题背景 微服务依赖 apollo-client 并完成配置.启动微服务之后,会每隔两分钟在控制台输出内容 " c.c.f.a.i.RemoteConfi ...

  10. nginx配置解析流程

    上一篇文章分析了nginx配置文件缓冲区的管理,接下来将详细分析nginx是如何解析配置文件的.包含模块上下文结构的创建.core核心模块的解析.event事件模块的解析.http模块的解析. 一.模 ...

最新文章

  1. Shiro第一个程序:官方快速入门程序Qucickstart详解教程
  2. 实现一个队列,使得push_rear(), pop_front() 和get_min()的时间复杂度为O(1)
  3. 如何遍历JavaScript对象?
  4. 洛谷P1133 教主的花园 动态规划
  5. Android之MVVM框架 - 数据绑定
  6. eclipse查看jar包源代码
  7. PyQt5 QTableView 全部item居中
  8. java 多层异常_Java多层嵌套异常处理的基本流程
  9. CSS 全解析实战(一)-导读
  10. 查询雇佣的所有员工_想要最好的员工? 让他们自己雇用
  11. python实例变量命名规则_Java,Python的一些命名规范
  12. SSIS - 7.发邮件任务
  13. 敏捷开发免费管理工具——火星人预览(四)
  14. java实验报告实验环境_Java实验报告一:Java开发环境的熟悉
  15. 聚类的基本概念-聚类与分类的区别
  16. C语言课程设计报告-菜单设计
  17. 台风怎么看内存颗粒_使用300多元的D4 16G内存是种什么体验
  18. 详解TCP数据包中SYN,ACK字段与数据发送的关联
  19. Vue后台管理通用模板
  20. 经典论文阅读笔记——VIT、Swin Transformer、MAE、CILP

热门文章

  1. YUV420P与YUVJ420P
  2. Groupon新变种 LevelUp:折价券
  3. (附源码)spring boot学科竞赛活动报名系统 毕业设计 012239
  4. 华为mate8 解锁+root手记
  5. 如何停止keepalived_systemctl无法停掉keepalived
  6. 韩国电影《醉画仙》,画仙的奋斗史。。。???
  7. 2018NOIOJ网站入门题表(大礼包汇总,更新ing)
  8. 市场上的智能语音助理,主要的工作原理是什么?
  9. Kafka学习征途:不再依赖ZK的KRaft
  10. JavaEE | 增强for循环