apollo local 模式_Apollo客户端使用与配置解析

本文详细介绍了Apollo客户端的使用,包括Apollo的local模式、配置解析,特别是`apollo.bootstrap.enabled=true`的含义和作用。通过实例展示了SpringBoot项目中Apollo的配置,分析了因缺少`apollo.bootstrap.namespaces`导致的问题,并提供了解决方案。同时,文章还提到了Apollo配置中心的设置,如`apollo.meta`的配置优先级。
摘要由CSDN通过智能技术生成

前言:

本文主要是分享Apollo Client客户端使用过程中,遇到的问题、解决问题及分析代码逻辑的过程。其中一个重要问题就是关于apollo.bootstrap.enabled = true的使用及注意事项。

一、准备工作

1.1环境要求

本文是基于Apollo v1.1.1版本,springboot项目客户端引入的是:

com.ctrip.framework.apollo

apollo-client

1.1.0

com.ctrip.framework.apollo

apollo-core

1.1.0

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.5springboot项目执行结果

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日志文件:

%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%T-%thread] %c [%L] -| %msg%n

二、引入问题

本来认为执行结果是:

+++++++++++++++++++++++++++++++++++++++++

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

Long> notificationsMap) {

Map 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 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。

本文纯属个人观点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值