zoukankan      html  css  js  c++  java
  • 携程Apollo(阿波罗)配置中心在Spring Boot项目快速集成

    前提:先搭建好本地的单机运行项目:http://www.cnblogs.com/EasonJim/p/7643630.html

    说明:下面的示例是基于Spring Boot搭建的,对于Spring项目基本通用。迁移旧项目的配置下一篇说明,这里先就如何快速的集成Client和获取配置的值进行实践。

    0、下面的示例都是基于官方提供的教程去实践的,可以参考如下网址:

    https://github.com/ctripcorp/apollo/wiki/Java%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97

    https://github.com/ctripcorp/apollo/wiki/Apollo%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97#23-java%E6%A0%B7%E4%BE%8B%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%90%AF%E5%8A%A8

    1、先新建好Spring Boot项目,这里使用了Web做测试,所以引用了Thymeleaf模板。

    搭建参考:http://www.cnblogs.com/EasonJim/p/7519854.html

    2、登录Apollo上新建App和相关的配置项,可以参考如下配置:

    3、在POM上引入Client的依赖,此时会有两个包引入Client和Core。

            <dependency>
                <groupId>com.ctrip.framework.apollo</groupId>
                <artifactId>apollo-client</artifactId>
                <version>0.8.0</version>
            </dependency>

    4、在项目上配置上面设置的app.id,注意此时是通过新建META-INF/app.properties文件实现的。

    5、代码实现

    实现的功能是通过配置中心修改后,再次刷新页面而不重启应用的情况下能看到值的改变。

    实现方式主要几种在两种,1为通过API的形式增加监听回调函数来监听值的改变后直接修改,2为通过注入Bean的方式使用Bean下的专用监听注解实现回调监听。

    5.1、通过API的方式

    代码实现如下:

    package com.jsoft.springboottest.springboottest1.controller;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    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;
    import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
    
    @Controller
    public class IndexController {
    
        private static final Logger logger = LoggerFactory.getLogger(IndexController.class);
    
        private Config config;
       private int timeout;
        private int batch;
        private String url;
    
        @RequestMapping(value = "/index", method = RequestMethod.GET)
        public String index(Model model) {
            Entry entry = new Entry();
            entry.setText("Text");
            entry.setTitle("Title");
            model.addAttribute("entries", entry);
            model.addAttribute("entry", new Entry());
            
            model.addAttribute("url", url);
            model.addAttribute("timeout",timeout);
            model.addAttribute("batch",batch);
    
            logger.info("timeout:{}", timeout);
            logger.info("batch:{}", batch);
            logger.info("url:{}", url);
    
            return "index";
        }
    
        public IndexController() {
            config = ConfigService.getAppConfig();
            config.addChangeListener(new ConfigChangeListener() {
                @Override
                public void onChange(ConfigChangeEvent changeEvent) {
                    logger.info("Changes for namespace {}", changeEvent.getNamespace());
                    for (String key : changeEvent.changedKeys()) {
                        ConfigChange change = changeEvent.getChange(key);
                        logger.info("Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType());
    
                        if (key.equals("url")) {
                            url = change.getNewValue();
                        }
                        if(key.equals("batch")) {
                            batch = Integer.valueOf(change.getNewValue());
                        }
                        if (key.equals("timeout")) {
                            timeout = Integer.valueOf(change.getNewValue());
                        }
                    }                    
                }
            });
        }
    
    }

    说明:

    通过新建Config对象,使用的是ConfigService.getAppConfig()获取默认配置,也就是配置中心中的application.properties的,当然,getAppConfig可以指定不能的namespage。

    通过获取Config对象增加监听回调函数addChangeListener。监听指定的值变化后,重新赋值变量。

    这种方式应该是最简单的,不用写特殊的注解去实现,但是可能在运行时也会发现,程序在一启动时不会去获取默认的配置值,比如timeout这些是空的,要解决这个问题时需要在Class上增加@EnableApolloConfig的注解,然后在属性上增加@Value的值即可,改动如下:

    package com.jsoft.springboottest.springboottest1.controller;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    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;
    import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
    
    @Controller
    @EnableApolloConfig
    public class IndexController {
    
        private static final Logger logger = LoggerFactory.getLogger(IndexController.class);
    
        private Config config;
    
        @Value("${timeout:200}")
        private int timeout;
        @Value("${batch:200}")
        private int batch;
        @Value("${url:http://easonjim.com}")
        private String url;
    
        @RequestMapping(value = "/index", method = RequestMethod.GET)
        public String index(Model model) {
            Entry entry = new Entry();
            entry.setText("Text");
            entry.setTitle("Title");
            model.addAttribute("entries", entry);
            model.addAttribute("entry", new Entry());
            
            model.addAttribute("url", url);
            model.addAttribute("timeout",timeout);
            model.addAttribute("batch",batch);
    
            logger.info("timeout:{}", timeout);
            logger.info("batch:{}", batch);
            logger.info("url:{}", url);
    
            return "index";
        }
    
        public IndexController() {
            config = ConfigService.getAppConfig();
            config.addChangeListener(new ConfigChangeListener() {
                @Override
                public void onChange(ConfigChangeEvent changeEvent) {
                    logger.info("Changes for namespace {}", changeEvent.getNamespace());
                    for (String key : changeEvent.changedKeys()) {
                        ConfigChange change = changeEvent.getChange(key);
                        logger.info("Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType());
    
                        if (key.equals("url")) {
                            url = change.getNewValue();
                        }
                        if(key.equals("batch")) {
                            batch = Integer.valueOf(change.getNewValue());
                        }
                        if (key.equals("timeout")) {
                            timeout = Integer.valueOf(change.getNewValue());
                        }
                    }                    
                }
            });
        }
    
    }

    说明:约定俗成,在@Value上获取值时记得增加默认值,以防止获取为空。

    上面代码或许有些冗余,可以再提炼一下,因为config对象的值会实时更新,所以也不需要监听onChange事件,也不需要自己创建一个变量,最后代码实现如下:

    package com.jsoft.springboottest.springboottest1.controller;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    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;
    import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
    
    @Controller
    public class IndexController2 {
    
        private static final Logger logger = LoggerFactory.getLogger(IndexController2.class);
    
        private Config config = ConfigService.getAppConfig();
    
        @RequestMapping(value = "/index2", method = RequestMethod.GET)
        public String index(Model model) {
            Entry entry = new Entry();
            entry.setText("Text");
            entry.setTitle("Title");
            model.addAttribute("entries", entry);
            model.addAttribute("entry", new Entry());
            
            model.addAttribute("url", config.getProperty("url", ""));
            model.addAttribute("timeout",config.getProperty("timeout", ""));
            model.addAttribute("batch",config.getProperty("batch", ""));
    
            logger.info("timeout:{}", config.getProperty("timeout", ""));
            logger.info("batch:{}", config.getProperty("batch", ""));
            logger.info("url:{}", config.getProperty("url", ""));
    
            return "index";
        }
    
    }

    5.2、通过注入Bean的方式

    5.2.1、新建的Bean如下:

    package com.jsoft.springboottest.springboottest1.controller;
    
    import com.ctrip.framework.apollo.Config;
    import com.ctrip.framework.apollo.model.ConfigChange;
    import com.ctrip.framework.apollo.model.ConfigChangeEvent;
    import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
    import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    
    import javax.annotation.PostConstruct;
    
    public class AnnotatedBean {
        private static final Logger logger = LoggerFactory.getLogger(AnnotatedBean.class);
    
        @Value("${timeout:200}")
        private int timeout;
        private int batch;
    
        @ApolloConfig
        private Config config;
        @ApolloConfig("FX.apollo")
        private Config anotherConfig;
    
        @PostConstruct
        void initialize() {
            logger.info("timeout is {}", timeout);
            logger.info("batch is {}", batch);
    
            logger.info("Keys for config: {}", config.getPropertyNames());
            logger.info("Keys for anotherConfig: {}", anotherConfig.getPropertyNames());
        }
    
        @Value("${batch:100}")
        public void setBatch(int batch) {
            this.batch = batch;
        }
    
        public int getBatch() {
            return batch;
        }
    
        public int getTimeout() {
            return timeout;
        }
    
        @ApolloConfigChangeListener("application")
        private void someChangeHandler(ConfigChangeEvent changeEvent) {
            logger.info("[someChangeHandler]Changes for namespace {}", changeEvent.getNamespace());
            if (changeEvent.isChanged("timeout")) {
                refreshTimeout();
            }
            if (changeEvent.isChanged("batch")) {
                setBatch(Integer.valueOf(changeEvent.getChange("batch").getNewValue()));
            }
        }
    
        @ApolloConfigChangeListener({ "application", "FX.apollo" })
        private void anotherChangeHandler(ConfigChangeEvent changeEvent) {
            logger.info("[anotherChangeHandler]Changes for namespace {}", changeEvent.getNamespace());
            for (String key : changeEvent.changedKeys()) {
                ConfigChange change = changeEvent.getChange(key);
                logger.info("[anotherChangeHandler]Change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType());
            }
        }
    
        private void refreshTimeout() {
            // do some custom logic to update placeholder value
            timeout = config.getIntProperty("timeout", timeout);
            logger.info("Refreshing timeout to {}", timeout);
        }
    }

    5.2.2、通过@Configuration注入这个Bean,代码如下:

    package com.jsoft.springboottest.springboottest1.controller;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
    
    @Configuration
    @EnableApolloConfig
    public class AnnotatedConfig {
        
        @Bean
        public AnnotatedBean annotatedBean() {
            return new AnnotatedBean();
        }
    }

    注意:要增加@EnableApolloConfig和@Configuration,不然不会生效。并且留意到@Bean的注解,如果没有这个时,@ApolloConfigChangeListener不会生效。这个是关键所在,@ApolloConfigChangeListener只能用于Bean注入上,这个和API的方式有明显区别。

    在代码上使用:

    package com.jsoft.springboottest.springboottest1.controller;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
        
        private static final Logger logger = LoggerFactory.getLogger(TestController.class);
        
        @Autowired
        private AnnotatedBean annotatedBean;
        
        @RequestMapping("/show")
        public String show(){
            
            logger.info("batch:{}",annotatedBean.getBatch());
            logger.info("timeout:{}",annotatedBean.getTimeout());
            
            return "Hello World"+" batch:"+annotatedBean.getBatch()+" timeout:"+annotatedBean.getTimeout();        
        }
    }

    说明:可以看出,只要@Autowired注入刚才的Bean就可以直接使用。

    6、运行,一般只需要在/opt/settings/server.properties中配置了env=DEV就可以直接直接启动(因为Client在本地仓库的包上已经有了meta_server的信息),但是在IDE上也可以通过指定VM的参数,增加系统属性变量-D来实现调试,配置如下:

    如果到处JAR运行,直接java -jar Spring-Boot-Demo.jar即可,不需要增加什么参数(但前提是配置了/opt/settings/server.properties的env的值)。

    测试代码:https://github.com/easonjim/5_java_example/tree/master/apollotest/test1 

  • 相关阅读:
    [转载]我的WafBypass之道(Misc篇)
    7.3 使用while 循环来处理列表和字典
    7-4__7-7练习
    7.2 while 循环
    第 7 章 用户输入和while 循环
    6.字典练习
    6.4 嵌套
    6.3 遍历字典
    6.2练习
    第 6 章 字典
  • 原文地址:https://www.cnblogs.com/EasonJim/p/7649047.html
Copyright © 2011-2022 走看看