Spring Boot 2.0 整合携程Apollo配置中心

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。

Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持。

.Net客户端不依赖任何框架,能够运行于所有.Net运行时环境。

如果想要深入了解,可以到github上参见Apollo配置中心,官网的介绍很详细。本章主要讲述Spring Boot 2.0 整合Apollo配置中心。

一、Apollo配置中心服务端(来源于官网)

本文的重点在于Apollo在客户端的使用,所以Apollo服务端使用的是官网提供的 Quick Start(针对本地测试使用),后续文章会专门讲述Apollo服务端在分布式环境下的部署。

1.1 准备工作

  1. Java
    Apollo服务端要求Java 1.8+,客户端要求Java 1.7+,笔者本地使用的是Java 1.8。
  2. MySQL
    Apollo的表结构对timestamp使用了多个default声明,所以需要5.6.5以上版本。笔者本地使用的是8.0.13版本
  3. 下载 Quick Start
    官网为我们准备了 Quick Start 安装包。大家只需要下载到本地,就可以直接使用,免去了编译、打包过程。大家可以到github下载,也可以通过百度云盘下载

1.2 安装步骤

1.2.1 创建数据库

Apollo服务端共需要两个数据库:ApolloPortalDB和ApolloConfigDB,官网把数据库、表的创建和样例数据都分别准备了sql文件(在下载的 Quick Start 安装包的sql目录下),只需要导入数据库即可。

1.2.1.1 创建ApolloPortalDB

通过各种Mysql客户端(Navicat,DataGrip等)导入sql/apolloportaldb.sql即可
下面以MySQL原生客户端为例:

source /your_local_path/sql/apolloportaldb.sql

导入成功后,可以通过执行以下sql语句来验证:

select `Id`, `AppId`, `Name` from ApolloPortalDB.App;
Id AppId Name
1 SampleApp Sample App
1.2.1.2 创建ApolloConfigDB

通过各种Mysql客户端(Navicat,DataGrip等)导入sql/apolloconfigdb.sql即可
下面以MySQL原生客户端为例:

source /your_local_path/sql/apolloconfigdb.sql

导入成功后,可以通过执行以下sql语句来验证:

select `NamespaceId`, `Key`, `Value`, `Comment` from ApolloConfigDB.Item;
NamespaceId Key Value Comment
1 timeout 100 sample timeout配置

1.2.2 配置数据库连接信息

Apollo服务端需要知道如何连接到你前面创建的数据库,所以需要编辑demo.sh,修改ApolloPortalDB和ApolloConfigDB相关的数据库连接串信息。

#apollo config db info
apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8
apollo_config_db_username=用户名
apollo_config_db_password=密码(如果没有密码,留空即可)

# apollo portal db info
apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8
apollo_portal_db_username=用户名
apollo_portal_db_password=密码(如果没有密码,留空即可)

1.3 启动Apollo配置中心

1.3.1 确保端口未被占用

Quick Start脚本会在本地启动3个服务,分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用。例如,在Linux/Mac下,可以通过如下命令检查:

lsof -i:8080

在windows下,可以通过如下命令检查:

netstat -aon|findstr "8080"

1.3.2 执行启动脚本

在Quick Start目录下执行如下命令:

./demo.sh start

当看到如下输出后,就说明启动成功了!

==== starting service ====
Service logging file is ./service/apollo-service.log
Started [10768]
Waiting for config service startup.......
Config service started. You may visit http://localhost:8080 for service status now!
Waiting for admin service startup....
Admin service started
==== starting portal ====
Portal logging file is ./portal/apollo-portal.log
Started [10846]
Waiting for portal startup......
Portal started. You can visit http://localhost:8070 now!

1.3.3 异常排查

如果启动遇到了异常,可以分别查看service和portal目录下的log文件排查问题。

注: 在启动apollo-configservice的过程中会在日志中输出eureka注册失败的信息,如com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused。需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。

1.4 使用Apollo配置中心

1.4.1 查看样例配置

  1. 浏览器访问http://localhost:8070
    登录界面
    登录界面

    Quick Start集成了Spring Security简单认证,更多信息可以参考Portal 实现用户登录功能
  2. 输入用户名apollo,密码admin登录
    项目界面
    项目界面

    配置中心中包含一个默认的项目SampleApp
  3. 点击SampleApp进入配置界面,可以看到当前有一个配置timeout=100


    项目配置详情
    项目配置详情

    如果提示系统出错,请重试或联系系统负责人,请稍后几秒钟重试一下,因为通过Eureka注册的服务有一个刷新的延时。

1.4.2 新增项目配置

我们的客户端使用apollo需要新增相关的项目配置。

  1. 点击新建项目


    项目信息
    项目信息
    • 应用ID:这个ID是应用的唯一标识
    • 应用名称:应用的名称,会展示在配置中心的首页上

    点击提交,创建完成


    应用配置界面
    应用配置界面
  2. 新增配置信息

    点击新增配置,填写配置信息
    配置信息
    配置信息
    点击提交,此时配置还未生效。
    未发布的配置
    未发布的配置
  3. 发布配置
    点击发布,配置立刻生效


    生效的配置
    生效的配置
  4. 回滚
    如果配置做了修改之后,发现配置更改错误,这个时候可以使用回滚功能,回到上一个配置


    回滚配置
    回滚配置

二、Apollo配置中心客户端

我们客户端基于Spring Boot 2.0搭建,开发工具是InteIIij IDEA。新建一个项目,项目名称为apollo-client

2.1 客户端搭建

  1. 添加Apollo客户端依赖
    <dependency>
         <groupId>com.ctrip.framework.apollo</groupId>
         <artifactId>apollo-client</artifactId>
         <version>1.1.1</version>
    </dependency>
    
    目前最新版本为1.1.1
  2. 添加配置信息
    # 应用ID(在Apollo服务端新增项目添加的应用ID)
    app.id=testclient
    # apollo-configservice地址
    apollo.meta=http://127.0.0.1:8080
    
  3. 开启Apollo客户端
    在项目的启动类上添加@EnableApolloConfig注解
  4. 新增一个测试接口
  @RequestMapping("/index")
  public String hello(){
    return "hello man";
  }
  1. 启动服务测试
    在Apollo配置中心中,我们对该项目有一条配置server.port = 9000,启动服务,访问http://localhost:9000/index,返回hello man。证明,客户端是从服务端获取的配置。

2.2 客户端用法

在上一节,我们简单的搭建了客户端,成功的使用服务端配置。Apollo为我们提供的使用方式有很多种,下面只介绍Spring Boot 2.0环境下的使用方式。

2.2.1 Spring Placeholder的使用

Spring应用通常会使用Placeholder来注入配置,使用的格式形如${someKey:someDefaultValue},如${timeout:100}。冒号前面的是key,冒号后面的是默认值(建议在实际使用时尽量给出默认值,以免由于key没有定义导致运行时错误)。Apollo从v0.10.0开始的版本支持placeholder在运行时自动更新。如果需要关闭placeholder在运行时自动更新功能,可以通过以下两种方式关闭:

  1. 通过设置System Property apollo.autoUpdateInjectedSpringProperties,如启动时传入-Dapollo.autoUpdateInjectedSpringProperties=false
  2. 通过设置META-INF/app.properties中的apollo.autoUpdateInjectedSpringProperties=false
2.2.1.1 Java Config使用方式
  1. 新建配置类JavaConfigBean如下:
    /**
     * Java Config方式
     *
     * @author simon
     * @create 2018-11-02 15:00
     **/
    @Configuration
    public class JavaConfigBean {
      @Value("${timeout:20}")
      private int timeout;
    
      public int getTimeout() {
        return timeout;
      }
    }
    
  2. 新增访问端点
      //1.Java Config方式
      @Autowired
      JavaConfigBean javaConfigBean;
    
      @RequestMapping("/index1")
      public String hello1(){
        return javaConfigBean.getTimeout()+"";
      }
    
  3. 测试
    浏览器访问http://127.0.0.1:8080/index1,正确返回配置的值
2.2.1.2 ConfigurationProperties使用方式

Spring Boot提供了@ConfigurationProperties把配置注入到bean对象中。Apollo也支持这种方式,下面的例子会把redis.cache.expireSecondsredis.cache.commandTimeout分别注入到SampleRedisConfigexpireSecondscommandTimeout字段中。

  1. 新增配置类SampleRedisConfig如下:
    /**
     * ConfigurationProperties使用方式
     *
     * @author simon
     * @create 2018-11-02 9:30
     **/
    @Configuration
    @ConfigurationProperties(prefix = "redis.cache")
    public class SampleRedisConfig {
      private int expireSeconds;
      private int commandTimeout;
    
      public void setExpireSeconds(int expireSeconds) {
        this.expireSeconds = expireSeconds;
      }
    
      public void setCommandTimeout(int commandTimeout) {
        this.commandTimeout = commandTimeout;
      }
    
      public int getExpireSeconds() {
        return expireSeconds;
      }
    
      public int getCommandTimeout() {
        return commandTimeout;
      }
    }
    
  2. 新增访问端点
    //2. ConfigurationProperties使用方式
      @Autowired
      SampleRedisConfig sampleRedisConfig;
    
      @RequestMapping("/index2")
      public String hello2(){
        return sampleRedisConfig.getCommandTimeout()+"--"+sampleRedisConfig.getExpireSeconds();
      }
    
  3. 测试
    浏览器访问http://127.0.0.1:8080/index2,正确返回配置的值

注: @ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEventRefreshScope。这个我会在后续文章中详细描述。

2.2.2 Spring Annotation支持

Apollo同时还增加了几个新的Annotation来简化在Spring环境中的使用。

  • @ApolloConfig用来自动注入Config对象
  • @ApolloConfigChangeListener用来自动注册ConfigChangeListener
  • @ApolloJsonValue用来把配置的json字符串自动注入为对象
2.2.2.1 @ApolloConfig的使用
  1. 新增访问端点
    // 3. @ApolloConfig使用
      @ApolloConfig
      private Config config;
    
      @RequestMapping("/index3")
      public String hello3(){
        Set <String> propertyNames = config.getPropertyNames();
        propertyNames.forEach(key -> {
          System.err.println(key+"="+config.getIntProperty(key,0));
        });
        return propertyNames.toString();
      }
    
  2. 测试
    浏览器访问http://127.0.0.1:8080/index3,正确打印配置的值
    redis.cache.commandTimeout=3000
    redis.cache.expireSeconds=20
    server.port=800
    timeout=200
    
2.2.2.2 @ApolloConfigChangeListener的使用
  1. 新增以下代码
    @ApolloConfigChangeListener
      private void someOnChange(ConfigChangeEvent changeEvent) {
        //update injected value of batch if it is changed in Apollo
        if (changeEvent.isChanged("timeout")) {
          System.out.println(config.getIntProperty("timeout", 0));
        }
      }
    
  2. 测试
    在Apollo服务端修改timeout配置的值为300,发布后,控制台打印300
2.2.2.3 @ApolloJsonValue的使用
  1. 新增User如下:
    /**
     * 用户
     *
     * @author simon
     * @create 2018-11-02 16:41
     **/
    public class User {
      private String username;
      private String password;
    
      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;
      }
    }
    
  2. 服务端新增配置
    jsonBeanProperty=[ { "username": "john", "password": "1234" }, { "username": "simon", "password": "222132" } ]
  3. 客户端获取配置
    //4. @ApolloJsonValue使用
    @ApolloJsonValue("${jsonBeanProperty:[]}")
    private List<User> anotherJsonBeans;
    
    @RequestMapping("/index4")
    public void hello4(){
      anotherJsonBeans.forEach(item -> {
        System.err.println(item.getUsername()+"--"+item.getPassword());
      });
    }
    
  4. 测试
    浏览器访问http://127.0.0.1:8080/index4,正确打印配置的值

源码下载

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,293评论 6 344
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong阅读 21,744评论 1 92
  • 转眼间毕业已经一个月了,忽然间发现了自己生活的颓丧,房间一直杂乱无章,心情也一样乌烟瘴气。躺着的时间比坐着多,整天...
    IloveMC阅读 141评论 0 1
  • 3.关于房子 这世界是不公平的,生活似乎对穷人没有一点怜悯之情。穷人,收入低,饮食方面,只能购买便宜的食物、甚至是...
    凌云雨轩阅读 207评论 0 0