Apollo配置中心(三)

本文讲述apollo在springboot项目中如何实现自动刷新

1.本文通过apollo在github上的apollo-demo项目来讲解

github地址:https://github.com/ctripcorp/apollo

2.代码解析

2.1 启动类代码解析
@SpringBootApplication(scanBasePackages = {"com.ctrip.framework.apollo.demo.spring.common",
"com.ctrip.framework.apollo.demo.spring.springBootDemo"
})
public class SpringBootSampleApplication {

  public static void main(String[] args) throws IOException {
    ApplicationContext context = new SpringApplicationBuilder(SpringBootSampleApplication.class).run(args);
    AnnotatedBean annotatedBean = context.getBean(AnnotatedBean.class);
    SampleRedisConfig redisConfig = null;
    try {
      redisConfig = context.getBean(SampleRedisConfig.class);
    } catch (NoSuchBeanDefinitionException ex) {
      System.out.println("SampleRedisConfig is null, 'redis.cache.enabled' must have been set to false.");
    }

    System.out.println("SpringBootSampleApplication Demo. Input any key except quit to print the values. Input quit to exit.");
    while (true) {
      System.out.print("> ");
      String input = new BufferedReader(new InputStreamReader(System.in, Charsets.UTF_8)).readLine();
      if (!Strings.isNullOrEmpty(input) && input.trim().equalsIgnoreCase("quit")) {
        System.exit(0);
      }

      System.out.println(annotatedBean.toString());
      if (redisConfig != null) {
        System.out.println(redisConfig.toString());
      }
    }
  }
}

这里主要通过上下文去获取bean,然后测试是否能拿到apollo的配置。

2.2 AnnotatedBean类代码解析
@Component("annotatedBean")
public class AnnotatedBean {
  private static final Logger logger = LoggerFactory.getLogger(AnnotatedBean.class);

  private int timeout;
  private int batch;
  private List<JsonBean> jsonBeans;

  /**
   * ApolloJsonValue annotated on fields example, the default value is specified as empty list - []
   * <br />
   * jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]
   */
  @ApolloJsonValue("${jsonBeanProperty:[]}")
  private List<JsonBean> anotherJsonBeans;

  @Value("${batch:100}")
  public void setBatch(int batch) {
    logger.info("updating batch, old value: {}, new value: {}", this.batch, batch);
    this.batch = batch;
  }

  @Value("${timeout:200}")
  public void setTimeout(int timeout) {
    logger.info("updating timeout, old value: {}, new value: {}", this.timeout, timeout);
    this.timeout = timeout;
  }

  /**
   * ApolloJsonValue annotated on methods example, the default value is specified as empty list - []
   * <br />
   * jsonBeanProperty=[{"someString":"hello","someInt":100},{"someString":"world!","someInt":200}]
   */
  @ApolloJsonValue("${jsonBeanProperty:[]}")
  public void setJsonBeans(List<JsonBean> jsonBeans) {
    logger.info("updating json beans, old value: {}, new value: {}", this.jsonBeans, jsonBeans);
    this.jsonBeans = jsonBeans;
  }

  @Override
  public String toString() {
    return String.format("[AnnotatedBean] timeout: %d, batch: %d, jsonBeans: %s", timeout, batch, jsonBeans);
  }

  private static class JsonBean{

    private String someString;
    private int someInt;

    @Override
    public String toString() {
      return "JsonBean{" +
          "someString='" + someString + '\'' +
          ", someInt=" + someInt +
          '}';
    }
  }
}

这个类主要甘肃我们 apollo的注解@ApolloJsonValue和spring的注解@Value都是可以拿到apollo的配置的,只是ApolloJsonValue可以解析json对象

2.4 SampleRedisConfig代码解析
@ConditionalOnProperty("redis.cache.enabled")
@ConfigurationProperties(prefix = "redis.cache")
@Component("sampleRedisConfig")
@RefreshScope
public class SampleRedisConfig {

  private static final Logger logger = LoggerFactory.getLogger(SampleRedisConfig.class);

  private int expireSeconds;
  private String clusterNodes;
  private int commandTimeout;

  private Map<String, String> someMap = Maps.newLinkedHashMap();
  private List<String> someList = Lists.newLinkedList();

  @PostConstruct
  private void initialize() {
    logger.info(
        "SampleRedisConfig initialized - expireSeconds: {}, clusterNodes: {}, commandTimeout: {}, someMap: {}, someList: {}",
        expireSeconds, clusterNodes, commandTimeout, someMap, someList);
  }

  public void setExpireSeconds(int expireSeconds) {
    this.expireSeconds = expireSeconds;
  }

  public void setClusterNodes(String clusterNodes) {
    this.clusterNodes = clusterNodes;
  }

  public void setCommandTimeout(int commandTimeout) {
    this.commandTimeout = commandTimeout;
  }

  public Map<String, String> getSomeMap() {
    return someMap;
  }

  public List<String> getSomeList() {
    return someList;
  }

  @Override
  public String toString() {
    return String.format(
        "[SampleRedisConfig] expireSeconds: %d, clusterNodes: %s, commandTimeout: %d, someMap: %s, someList: %s",
            expireSeconds, clusterNodes, commandTimeout, someMap, someList);
  }
}

我们也可以通过@ConfigurationProperties注解,通过前缀的方式拿到apollo的配置,set bean的属性。

3.测试

先直接启动看下效果,按下回车,控制台输出:

[AnnotatedBean] timeout: 200, batch: 100, jsonBeans: []
3.1 配置apollo

在配置文件中,配置

app:
    id: 你的appid

然后修改MetaDomainConsts中的 DEFAULT_META_URL 常量(你的meta server地址)
并在apollo后台新增如下配置:

timeout: 1000, batch: 200, 
jsonBeanProperty: [{omeString: '111',omeInt: 222}],
redis.cache.expireSeconds: 55555

再次启动看效果,控制台输出:

[AnnotatedBean] timeout: 1000, batch: 200, jsonBeans: [JsonBean{someString='111', someInt=222}]

AnnotatedBean的属性值都拿到了,但是SampleRedisConfig中的expireSeconds属性并没有拿到

3.2 @ConditionalOnProperty注解

Spring Boot通过@ConditionalOnProperty来控制Configuration是否生效

在看下SampleRedisConfig类上有@ConditionalOnProperty(“redis.cache.enabled”),恍然大悟,需要配置redis.cache.enabled中的属性为true才会生效,所以我们在后台继续新增配置redis.cache.enabled:true。
这是控制台输出结果如下

[AnnotatedBean] timeout: 1000, batch: 200, jsonBeans: [JsonBean{someString='111', someInt=222}]
[SampleRedisConfig] expireSeconds: 55555, clusterNodes: null, commandTimeout: 1111, someMap: {}, someList: []
4 实时刷新
4.1通过后台修改配置
timeout:2000,expireSeconds:88888

看控制台立马输出了日志

[apollo-demo][Apollo-Config-6]2020-03-08 13:47:16,334 INFO  [com.ctrip.framework.apollo.demo.spring.common.bean.AnnotatedBean] updating timeout, old value: 1000, new value: 2000
[apollo-demo][Apollo-Config-6]2020-03-08 13:47:16,334 INFO  [com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener] Auto update apollo changed value successfully, new value: 2000, key: timeout, beanName: annotatedBean, method: com.ctrip.framework.apollo.demo.spring.common.bean.AnnotatedBean.setTimeout
[apollo-demo][Apollo-Config-2]2020-03-08 13:32:34,605 INFO  [com.ctrip.framework.apollo.demo.spring.springBootDemo.refresh.SpringBootApolloRefreshConfig] before refresh [SampleRedisConfig] expireSeconds: 55555, clusterNodes: null, commandTimeout: 1111, someMap: [SampleRedisConfig] expireSeconds: 55555, clusterNodes: null, commandTimeout: 1111, someMap: {}, someList: [], someList: []
[apollo-demo][Apollo-Config-2]2020-03-08 13:32:34,609 INFO  [com.ctrip.framework.apollo.demo.spring.springBootDemo.config.SampleRedisConfig] SampleRedisConfig initialized - expireSeconds: 88888, clusterNodes: null, commandTimeout: 1111, someMap: 88888, someList: []
[apollo-demo][Apollo-Config-2]2020-03-08 13:32:34,609 INFO  [com.ctrip.framework.apollo.demo.spring.springBootDemo.refresh.SpringBootApolloRefreshConfig] after refresh [SampleRedisConfig] expireSeconds: 88888, clusterNodes: null, commandTimeout: 1111, someMap: [SampleRedisConfig] expireSeconds: 88888, clusterNodes: null, commandTimeout: 1111, someMap: {}, someList: [], someList: []

回车空控制台输出

[AnnotatedBean] timeout: 2000, batch: 200, jsonBeans: [JsonBean{someString='111', someInt=222},]
[SampleRedisConfig] expireSeconds: 88888, clusterNodes: null, commandTimeout: 1111, someMap: {}, someList: [

发现timeout的值和expireSeconds的值实时更新了

4.2 SpringBootApolloRefreshConfig类

值得注意的是,在官方文档中是这样介绍的

@ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEvent或RefreshScope

4.3 客户端设计
1.客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
2.客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
    这是一个fallback机制,为了防止推送机制失效导致配置不更新
    客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
    定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
3.客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
4.客户端会把从服务端获取到的配置在本地文件系统缓存一份
    在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
5.应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知

欢迎扫描下面图片关注我的个人公众号,回复“资源”可以获取java核心知识整理和经典书籍

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值