Apollo项目实战

Apollo项目实战

Apollo项目实战

1 Apollo应用

Apollo项目实战

项目地址:https://github.com/ctripcorp/apollo
使用手册:https://github.com/ctripcorp/apollo/wiki

1.1 初识Apollo

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。
Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持。

1)核心功能:

统一管理不同环境、不同集群配置: 
    1:Apollo提供了一个统一界面集中式管理不同环境(environment)、不同集群(cluster)、不同命名空间(namespace)的配置。 
    2:同一份代码部署在不同的集群,可以有不同的配置,比如zk的地址等 
    3:通过命名空间(namespace)可以很方便的支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖 
    4:配置界面支持多语言(中文,English)
    
热发布:
    用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序。 
    
版本发布管理: 
    所有的配置发布都有版本概念,从而可以方便的支持配置的回滚。 
    
灰度发布: 
    支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例。 
    
权限管理、发布审核、操作审计: 
   1:应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误。 
   2:所有的操作都有审计日志,可以方便的追踪问题。 
客户端配置信息监控: 
   可以方便的看到配置在被哪些实例使用 
提供Java和.Net原生客户端: 
    1:提供了Java和.Net的原生客户端,方便应用集成 
    2:支持Spring Placeholder,Annotation和Spring Boot的ConfigurationProperties,方便应用使用(需要Spring 3.1.1+) 
    3:同时提供了Http接口,非Java和.Net应用也可以方便的使用 
    
提供开放平台API: 
    1:Apollo自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。 
    2:不过Apollo出于通用性考虑,对配置的修改不会做过多限制,只要符合基本的格式就能够保存。 
    3:在我们的调研中发现,对于有些使用方,它们的配置可能会有比较复杂的格式,如xml, json,需要对格式做校验。 
    4:还有一些使用方如DAL,不仅有特定的格式,而且对输入的值也需要进行校验后方可保存,如检查数据库、用户名和密码是否匹配。 
    5:对于这类应用,Apollo支持应用方通过开放接口在Apollo进行配置的修改和发布,并且具备完善的授权和权限控制 
    
部署简单: 
   1:配置中心作为基础服务,可用性要求非常高,这就要求Apollo对外部依赖尽可能地少 
   2:目前唯一的外部依赖是MySQL,所以部署非常简单,只要安装好Java和MySQL就可以让Apollo跑起来 
   3:Apollo还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数

2)谁在用它
国内很多大厂都在用Apollo作为分布式配置中心,如果你们的网站也在用,在https://github.com/ctripcorp/apollo/issues/451可以进行登记。
携程、华为、京东、智联招聘、中国移动、360金融、中通快递、转转、贝壳网、土巴兔、平安银行、有赞等众多国内大厂在用Apollo。
Apollo项目实战

1.2 Apollo单机部署

我们接下来实现Apollo安装,安装前我们先介绍一下单机版装的架构,如下图:
Apollo项目实战
上图展示了Apollo单机部署架构,我们对其中每个节点进行解释说明:

Apollo Config Service:提供配置的读取、推送等功能,服务对象是 Apollo 客户端。 
Apollo Admin Service:提供配置的修改、发布等功能,服务对象是Apollo Portal。 
Apollo Portal:Apollo 的管理界面,进行不同项目的配置(项目配置、权限配置等),服务对象是开 
发者和开放平台API

环境说明:

MySQL:MySQL建议用5.7,版本要求5.6.5+ 
Apollo服务端:JDK1.8+ 
Apollo客户端:JDK1.7+

Apollo安装方式有多种,官方提供了快速安装模式和Docker安装模式,我们把两种安装模式都实现一次,但如果是生产环境请使用分布式部署方
https://github.com/ctripcorp/apollo/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97

1.2.1 快速安装

1)JDK版本:

[root@skywalking ~]# java -version 
java version "1.8.0_144" 
Java(TM) SE Runtime Environment (build 1.8.0_144-b01) 
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

2)MySQL版本:

SELECT VERSION() 
----------------- 
5.7.32-log

3)安装包下载:
Apollo已经准备好了一个Quick Start安装包 apollo-quick-start.zip ,里面包含了可以自动启动的jar包、以及所有依赖jar包、数据库脚本、内置Tomcat容器等,安装包共63M,大家只需要下载到本地,就可以直接使用,免去了编译、打包过程。
Github下载地址:https://github.com/nobodyiam/apollo-build-scripts
Quick Start只针对本地测试使用,所以一般用户不需要自己下载源码打包,只需要下载已经打好的包即可。不过也有部分用户希望在修改代码后重新打包,那么可以参考如下步骤:

1:修改apollo-configservice, apollo-adminservice和apollo-portal的pom.xml,注释掉 
spring-boot-maven-plugin和maven-assembly-plugin 
2:在根目录下执行mvn clean package -pl apollo-assembly -am -DskipTests=true 
3:复制apollo-assembly/target下的jar包,rename为apollo-all-in-one.jar

4)创建数据库:
Apollo服务端共需要两个数据库: ApolloPortalDB 和 ApolloConfigDB ,我们把数据库、表的创建和样例数据都分别准备了sql文件,只需要导入数据库即可。
注意:如果你本地已经创建过Apollo数据库,请注意备份数据。我们准备的sql文件会清空Apollo相关的表。
Apollo项目实战
5)配置数据库连接:
我们的数据库地址不是固定的,Apollo服务端需要知道如何连接到你前面创建的数据库,因此需要修改数据库连接地址,在安装包里有一个启动脚本 demo.sh ,修改ApolloPortalDB和ApolloConfigDB相关的数据库连接串信息:

# apollo config db info 
apollo_config_db_url="jdbc:mysql://192.168.211.145:3306/ApolloConfigDB? 
characterEncoding=utf8&serverTimezone=Asia/Shanghai" 
apollo_config_db_username=root 
apollo_config_db_password=123456 
# apollo portal db info 
apollo_portal_db_url="jdbc:mysql://192.168.211.145:3306/ApolloPortalDB? 
characterEncoding=utf8&serverTimezone=Asia/Shanghai" 
apollo_portal_db_username=root 
apollo_portal_db_password=123456

6)启动服务:
Quick Start脚本会在本地启动3个服务,分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用,如果端口没有被使用,我们可以直接启动程序,启动程序执行 demo.sh 脚本即可,启动过程比较慢。

./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!

启动完成后访问http://192.168.211.145:8070/,可以看到Apollo配置界面
Apollo项目实战

1.2.2 Docker容器安装

如果您对Docker非常熟悉,可以使用Docker的方式快速部署Apollo,从而快速的了解Apollo。确保docker-quick-start文件夹已经在本地存在,如果本地已经clone过Apollo的代码,则可以跳过此步骤。
在docker-quick-start目录下执行 docker-compose up ,第一次执行会触发下载镜像等操作,需要耐心等待一些时间。
搜索所有 apollo-quick-start 开头的日志,看到以下日志说明启动成功:

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

不过基于Docker安装需要注意一些问题:

1:数据库的端口映射为13306,所以如果希望在宿主机*问数据库,可以通过localhost:13306,用户 
名是root,密码留空。 
2:如要查看更多服务的日志,可以通过docker exec -it apollo-quick-start bash登录, 然后 
到/apollo-quick-start/service和/apollo-quick-start/portal下查看日志信息。

安装完成后访问http://192.168.211.145:8070/,可以看到Apollo配置界面

2 配置中心使用

我们接下来学习Apollo配置中心使用,配置中心主要用于对分布式应用提供配置服务,访问地址是http://192.168.211.145:8070,登录账号apollo,密码admin
Apollo项目实战
登录后界面如下:
Apollo项目实战

2.1 项目配置托管

我们接下来使用Apollo作为配置中心,将项目配置托管到Apollo中。

2.2.1 配置创建

Apollo项目实战
点击创建应用,会出现如下表单:
Apollo项目实战
创建应用参数说明:

  • 部门:选择应用所在的部门。部门数据来自 ApolloPortalDB 库的 ServerConfig 表的 Key = organizations 对应的记录。
  • 应用 AppId:用来标识应用身份的唯一 id,格式为 string,需要和客户端 app.properties 中配 置的app.id 对应。
  • 应用名称:应用名,仅用于界面展示。
  • 应用负责人:默认具有项目管理员权限。
  • 项目管理员:可以创建 Namespace 和集群、分配用户权限。

默认配置
创建配置就需要将项目中的配置挪到Apollo中,可以点击添加配置按钮,默认每个项目都有一个默认配置 application.properties ,如下图:
Apollo项目实战
添加配置如下图:
Apollo项目实战
此时就相当于在 application.properties 中添加了一个key:value数据:

#bootstrap.properties 
message=黑马顺风车,日活4.5亿架构 

配置保存后,可以看到配置信息,但项目中还无法直接读取数据,必须发布之后才能读取,如下图,还可以查看每次修改的历史记录、哪些项目在使用该实例、修改配置、删除当前配置文件,每个配置文件都被定义为namespace:
Apollo项目实战
公共配置创建
除了默认的配置,我们还可以自己创建更多配置,创建新配置点击 添加Namespace 即可出现如下添加界面:
Apollo项目实战
在这里类型有2个选项,一个是创建公共(public)配置,一个是创建私有(private)配置,关于他们的区别如下:

public: 
公共的Namespace的配置能被任何项目读取 
通过创建公共Namespace可以实现公共组件的配置,或多个应用共享同一份配置的需求 
如果其它应用需要覆盖公共部分的配置,可以在其它应用那里关联公共Namespace,然后在关联的 
Namespace里面配置需要覆盖的配置即可 
如果其它应用不需要覆盖公共部分的配置,那么就不需要在其它应用那里关联公共Namespace 
private: 
私有Namespace的配置只能被所属的应用获取到 
通过创建一个私有的Namespace可以实现分组管理配置 
私有Namespace的格式可以是xml、yml、yaml、json、txt. 您可以通过apollo-client中 
ConfigFile接口来获取非properties格式Namespace的内容 
1.3.0及以上版本的apollo-client针对yaml/yml提供了更好的支持,可以通过 
ConfigService.getConfig("someNamespace.yml")直接获取Config对象,也可以通过 
@EnableApolloConfig("someNamespace.yml")或 
apollo.bootstrap.namespaces=someNamespace.yml注入yml配置到Spring/SpringBoot中去

我们接下来可以添加部分数据并发布,如下图:
Apollo项目实战
配置关联
我们接下来创建配置关联公共配置,我们可以先创建一个项目 hailtaxi-driver-config ,在该项目中创建一个私有配置 ipinfo 继承上面的stmt公共配置:
Apollo项目实战
我们再添加一部分数据如下图:
Apollo项目实战
私有配置
我们接下来可以为 hailtaxi-driver-config 创建一个私有配置,如下图:
Apollo项目实战
我们添加部分信息如下图:
Apollo项目实战

2.2.2 SpringBoot集成Apollo

配置使用案例
为了将更多资源交给Apollo,我们在 hailtaxi-driver-config 中添加一个新的namespace,用于配置springboot的核心配置 springboot-config 内容如下:
Apollo项目实战
配置数据如下:

spring.application.name = hailtaxi-driver 
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver 
spring.datasource.url = jdbc:mysql://192.168.211.145:3306/hailtaxi-driver? 
useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC 
spring.datasource.username = root 
spring.datasource.password = 123456 
spring.cloud.consul.host = 192.168.211.145 
spring.cloud.consul.port = 8500 
spring.cloud.consul.discovery.service-name = ${spring.application.name} 
spring.cloud.consul.discovery.prefer-ip-address = true 

我们编写一个案例,获取配置中的数据(stmt、driver-info),并对外输出友好提示,在 hailtaxi- driver 中创建 com.itheima.driver.controller.TripController 代码如下:

@RestController
@RequestMapping(value = "/trip")
@ConfigurationProperties(prefix = "platform")
@Data
public class TripController {

    @Autowired
    private Environment environment;

    /***
     * 出行提醒
     * @return
     */
    @GetMapping(value = "/message")
    public String message(){
        //获取hailtaxi-driver-config中stmt数据
        String ip = environment.getProperty("ip");
        String city = environment.getProperty("city");
        String weather = environment.getProperty("weather");

        //获取hailtaxi-driver-config中driver-info数据
        String drivertype = environment.getProperty("drivertype");
        String driveryear = environment.getProperty("driveryear");

        //组装提示信息
        String message = "您的IP【"+ip+"】,欢迎【"+
                city+"】用户,当前天气"+weather+"。您选择的车型为【"+
                drivertype+"】,该车型的司龄"+driveryear+",祝您出行愉快!【"+
                name+"】来自于【"+coursename+"】,联系地址:"+address;
        return message;
    }
}

集成Apollo
集成Apollo有2个步骤,首先是要引入Apollo的客户端,接着需要在springboot核心配置文件中引入需要从Apollo中注入的配置信息。

1)引入依赖
在 hailtaxi-driver 的pom.xml中引入如下依赖:

 <!--ApolloClient-->
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>1.7.0</version>
        </dependency>

2)修改配置
在 bootstrap.yml 中去掉如下配置:

spring:
  application:
    name: hailtaxi-driver
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.211.145:3306/hailtaxi-order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
  cloud:
    #Consul配置
    consul:
      host: 192.168.211.145
      port: 8500
      discovery:
        #注册到Consul中的服务名字
        service-name: ${spring.application.name}
        prefer-ip-address: true

在 bootstrap.yml 中添加如下配置:

app:
  id: hailtaxi-driver-config  #使用的 Apollo 的项目(应用)编号
apollo:
  meta: http://192.168.211.145:8080 #Apollo Meta Server 地址
  bootstrap:
    enabled: true   #是否开启 Apollo 配置预加载功能。默认为 false。
    eagerLoad:
      enable: true  #是否开启 Apollo 支持日志级别的加载时机。默认为 false。
    namespaces: application,stmt,driver-info,springboot-config  #使用的 Apollo 的命名空间,默认为 application。

完整配置文件如下:

server:
  port: 18081
#Apollo应用
app:
  id: hailtaxi-driver-config  #使用的 Apollo 的项目(应用)编号
apollo:
  meta: http://192.168.211.145:8080 #Apollo Meta Server 地址
  bootstrap:
    enabled: true   #是否开启 Apollo 配置预加载功能。默认为 false。
    eagerLoad:
      enable: true  #是否开启 Apollo 支持日志级别的加载时机。默认为 false。
    namespaces: application,stmt,driver-info,springboot-config  #使用的 Apollo 的命名空间,默认为 application。

我们请求 http://localhost:18081/trip/message 进行测试,效果如下:
Apollo项目实战

2.2 Apollo实用功能

在真实项目应用中,有很多需要注意的地方,比如配置自动刷新、配置加载顺序、复杂环境配置、数据加密、日志配置等都会困扰我们生产环境,我们接下来学习下这些问题的应对方案。
自动刷新
前面我们已经实现了SpringBoot读取配置,但我们用的是 Environment ,我们分别使用一下 @Value 和 @ConfigurationProperties ,再观察下配置是否会刷新。

我们在 stmt 中增加如下数据:

#使用@ConfigurationProperties的配置 
platform.name=黑马顺风车 
platform.address=北京金燕龙科研大楼 
platform.coursename=黑马架构师课程

Apollo项目实战
我们分别修改 ip 、 city 、 platform.name ,并发布,如下图:
Apollo项目实战
我们可以发现此时 ip 、 city 发生了变化,而 plateform.name 未发生变化,如下图:
Apollo项目实战
目前 Apollo 暂时未提供 @ConfigurationProperties 注解的配置类的自动刷新配置的功能,并且在纯 Spring Boot 项目中,没有太好的实现自动刷新配置的方式,所以大家以后用Apollo作为配置中心,建议使用 @Value 或者使用 Environment 加载配置,如果有些场景必须要用到@ConfigurationProperties 就需要结合Apollo的监听器解决。

Apollo监听器
Apollo 已经能够满足我们绝大多数场景下的自动刷新配置的功能,但是项目中如果使用了@ConfigurationProperties 此时只能借助Apollo的监听器功能实现数据刷新,可以先在监听器中读取变更的内容,然后调用指定的set方法执行重新赋值操作。
我们在 hailtaxi-driver 中创建监听器com.itheima.driver.apollo.ApolloConfigListener ,代码如下:

@Component
public class ApolloConfigListener {

    /***
     * 数据变更监听
     * @param changeEvent
     * @throws Exception
     */
    @ApolloConfigChangeListener(
            value = {"stmt", "driver-info"},        //侦听指定文件变更
            interestedKeyPrefixes = {"platform."})  //以plateform.开始的key发生变更才监听
    public void onChange(ConfigChangeEvent changeEvent) throws Exception {
        //获得 Apollo 所有配置项
        Set<String> strings = changeEvent.changedKeys();
        for (String key : strings) {
            System.out.println("变更的Key="+key);
            System.out.println("变更前的value="+changeEvent.getChange(key).getOldValue());
            System.out.println("变更后的value="+changeEvent.getChange(key).getNewValue());
        }
    }

}

我们修改 stmt 配置,修改后记得发布,如下操作:
Apollo项目实战
在项目中我们调试结果如下:
Apollo项目实战
控制台输出:

变更的Key=platform.address 
变更前的value=北京金燕龙科研大楼 
变更后的value=北京金燕龙科研大楼-北京总部 

配置加密
考虑到安全性,我们可能最好将配置文件中的敏感信息进行加密。例如说,MySQL 的用户名密码、第三方平台的 Token 令牌等等。不过,Apollo 暂时未内置配置加密的功能。官方文档说明如下:
Apollo项目实战
项目地址:https://github.com/ctripcorp/apollo-use-cases/tree/master/spring-boot-encrypt

1)密文获取
我们先使用StringEncryptor获取密文,可以先创建一个明文,对明文进行加密,在 stmt 中创建jasypt.encryptor.password=123456 ,在程序中对它进行加密。

加密测试程序:

@SpringBootTest
@RunWith(SpringRunner.class)
public class PasswordTest {

    @Autowired
    private StringEncryptor encryptor;

    @Test
    public void encode() {
        String password = "123456";
        System.out.println(encryptor.encrypt(password));
    }
}

测试运行输出的密文: bjLvhdN3k4N2f5wPzlB0VAPmVYQd6o+pyRm24S41rPQG4DIkHH3iwFOXL0k1URti
2)密文输入
我们将密文配置到Apollo中,以 ENC(密文) 修饰,我们对数据连接中密码进行加密,如下图:
Apollo项目实战
接下来我们运行hailtaxi-driver程序,从数据库获取指定司机信息
http://localhost:18081/driver/info/1

配置加载顺序
Apollo多个Namespace加载顺序是存在差异,我们可以在多个namespace中创建相同的key来测试加载过程。如下图:
Apollo项目实战
我们配置2个文件,分别为application的drivertype和driver-info的drivertype,我们看看程序中的配置:

server:
  port: 18081
#Apollo应用
app:
  id: hailtaxi-driver-config  #使用的 Apollo 的项目(应用)编号
apollo:
  meta: http://192.168.211.145:8080 #Apollo Meta Server 地址
  bootstrap:
    enabled: true   #是否开启 Apollo 配置预加载功能。默认为 false。
    eagerLoad:
      enable: true  #是否开启 Apollo 支持日志级别的加载时机。默认为 false。
    namespaces: application,stmt,driver-info,springboot-config  #使用的 Apollo 的命名空间,默认为 application。

在代码中,我们去获取了 Spring Environment对象,因为我们要从其中获取到PropertySource配置来源。

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(basePackages = "com.itheima.driver.mapper")
public class DriverApplication {

    public static void main(String[] args) {
        ApplicationContext act = SpringApplication.run(DriverApplication.class,args);
        Environment environment = act.getEnvironment();
        System.out.println(environment);
    }
}

DEBUG 运行 Application,并记得在 System.out.println(environment); 代码块打一个断点,可以
看到如下图的调试信息:
Apollo项目实战
我们可以得出如下结论:

1:对于 apollo.bootstrap 对应一个 CompositePropertySource 对象,即使有对应多个 Apollo Namespace。并且,多个 Namespace 是按照在 apollo.bootstrap.namespaces 配置顺序。 
2:所有 Apollo 对应的 PropertySource 对象,优先级非常高,目前看下来仅仅低于 server.ports 对应的 MapPropertySource。基本上,我们可以认为是最高优先级了。

灰度发布
通过创建灰度版本,您可以对某些配置做灰度测试 灰度流程为:

1.创建灰度版本 
2.配置灰度配置项 
3.配置灰度规则.如果是私有的namespace可以按照客户端的IP进行灰度,如果是公共的namespace则可以同时按AppId和客户端的IP进行灰度 
4.灰度发布

灰度版本最终有两种结果:全量发布和放弃灰度 全量发布:灰度的配置合到主版本并发布,所有的客户端都会使用合并后的配置 放弃灰度:删除灰度版本,所有的客户端都会使用回主版本的配置 注意事项:如果灰度版本已经有灰度发布过,那么修改灰度规则后,无需再次灰度发布就立即生效
Apollo项目实战
如下图,如果测试版本有额外的配置,我们可以点击新增灰度配置
Apollo项目实战
新增配置后,点击灰度发布才会生效
Apollo项目实战
我们可以让指定的IP的服务为灰度版本,如下图可以选择当前访问过服务的IP,如果有其他指定IP,可以手动直接输入,手动输入多个IP以逗号隔开,当用户访问这些IP指定的服务时,对应配置才会生效。
Apollo项目实战
手动输入IP
Apollo项目实战
全量灰度发布在现实工作中是指灰度版本没有问题了,需要把所有服务的版本全部切换成完成测试的灰度版本,我们点击全量发布即可,全量发布的时候,我们可以把灰度版本删除。
Apollo项目实战

Apollo加载流程

配置加载流程:
spring.factories配置要启动加载的类->ApolloApplicationContextInitializer implements ApplicationContextInitializer Spring容器刷新之前,会执行该对象的方法

ApolloApplicationContextInitializer:
	1:获取ConfigurableEnvironment,配置文件信息
	2:Config config = ConfigService.getConfig(namespace);->执行远程请求(Http请求->ConfigServiceLocator.updateConfigServices)获取Apollo服务配置信息
	3:获取到远程配置文件,解析配置文件存储到Environment中
public class ApolloApplicationContextInitializer implements
    ApplicationContextInitializer<ConfigurableApplicationContext> , EnvironmentPostProcessor, Ordered {
  public static final int DEFAULT_ORDER = 0;

  private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
  private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
  private static final String[] APOLLO_SYSTEM_PROPERTIES = {"app.id", ConfigConsts.APOLLO_CLUSTER_KEY,
      "apollo.cacheDir", "apollo.accesskey.secret", ConfigConsts.APOLLO_META_KEY, PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE};

  private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
      .getInstance(ConfigPropertySourceFactory.class);

  private int order = DEFAULT_ORDER;

  @Override
  public void initialize(ConfigurableApplicationContext context) {
    ConfigurableEnvironment environment = context.getEnvironment();

    if (!environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false)) {
      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);

    initialize(environment);
  }


  /**
   * Initialize Apollo Configurations Just after environment is ready.
   *
   * @param environment
   */
  protected void initialize(ConfigurableEnvironment environment) {

    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);
  }

 public static Config getConfig(String namespace) {
    return s_instance.getManager().getConfig(namespace);
  }

Apollo项目实战

上一篇:Apollo 自动驾驶开发套件(D-KIT)


下一篇:玩转apollo配置中心——springboot整合Apollo实现集中配置自动化更新