Apollo的基本使用方法

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

本文将通过一个SpringBoot的示例工程,简单介绍Apollo的基本使用方法,包括创建项目、托管和注入配置,以及配置更新事件的监听和处理。

一、环境描述

1. Apollo服务器

2. Spring Tool Suite

  • 版本:STS 3.9.2.RELEASE

3. Redis

  • IP(实例-1):10.15.1.21
  • 端口(实例-1):6379
  • IP(实例-2):192.168.190.128
  • 端口(实例-2):6379
  • 安装方法:参考《Redis的Docker镜像制作详解

二、创建和配置SpringBoot示例工程

这个示例工程在启动时会加载托管在Apollo中的配置文件,能够向指定的Redis缓存写入和读取数据,并且在修改Redis连接配置之后,不用重新启动应用,即可实现配置热更新,自动重连Redis缓存。

1. 新建示例工程

通过STS建立SpringBoot的示例工程,名称为apollo-springboot-demo。本文采用的配置如下:

  • SpringBoot版本为2.0.1
  • 项目依赖关系选择Web

具体的创建步骤,本文不再详述。

2. 添加依赖关系

由于Apollo官方没有将apollo-client.jar文件上传至Maven的中央仓库,因此需要参考《如何编译安装Apollo服务器(单机版)》文档,自行编译和打包Apollo客户端的jar包文件,然后上传至公司的Maven私服。

apollo-client的依赖关系,如下图所示:

Apollo客户端的依赖关系

如果已经将客户端jar包上传至Maven私服,则向pom.xml文件添加以下依赖关系:

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

如果尚未将客户端jar包上传至Maven私服,则向pom.xml文件添加以下依赖关系:

        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>0.11.0-SNAPSHOT</version>
            <scope>system</scope>
            <systemPath>D:\apollo-client\apollo-client.jar</systemPath>
        </dependency>

        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-core</artifactId>
            <version>0.11.0-SNAPSHOT</version>
            <scope>system</scope>
            <systemPath>D:\apollo-client\apollo-core.jar</systemPath>
        </dependency>

        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>

注意,apollo-client.jar和apollo-core.jar文件存放在本机的D:\apollo-client目录之中。

3. 配置应用标识符

app.id是应用的身份信息,是从Apollo服务端获取配置的一个重要条件。在src/main/resources/META-INF目录中创建app.properties文件,内容如下所示:

app.id=100006666

注意,每个业务系统的app.id不能重复,并且必须和Apollo服务端创建的项目的app.id保持一致。

文件位置如下图所示:

app.properties文件的位置

Apollo客户端也支持通过VM运行参数传入app.id信息,例如:

-Dapp.id=100006666

本文采用前一种方式,将app.id指定为100006666

4. Bootstrap阶段注入配置

Apollo客户端可以将application.properties文件中的配置托管至Apollo服务端。本文会托管SpringBoot的服务器端口号,需要在示例工程的application.properties文件中添加以下配置:

apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application

上述配置表示在示例工程启动的bootstrap阶段,向Spring容器注入被托管的application.properties文件的配置信息。

注意,业务系统中的每个配置文件都对应于Apollo服务端中相应项目的命名空间(namespace),例如:application.properties文件对应于application命名空间。

5. postProcessBeanFactory阶段注入配置

在一般情况下,Apollo会在Spring的postProcessBeanFactory阶段将大多数配置注入至Spring容器中。这个阶段早于bean的初始化阶段,已经能够很好地满足普通的bean注入配置场景。

本文将Redis缓存的连接配置(IP地址和端口号)托管至Apollo服务端,并且会在示例工程启动的postProcessBeanFactory阶段注入这些配置。

在示例工程中,新建com.example.demo.config.loader包,在该包中新建ConfigLoader类,关键代码如下所示:

@Configuration
@EnableApolloConfig(value = "redis")
public class ConfigLoader {
}

上述类是一个空类,功能全部由@EnableApolloConfig注解实现了。这个注解会在示例工程启动时,将Apollo服务端的redis命名空间中的配置注入Spring容器,也就相当于加载传统的redis.properties配置文件。

注意,@EnableApolloConfig注解要和@Configuration注解一起使用,否则不会生效。

将Redis缓存的连接配置注入Spring容器之后,还不能直接使用,需要在com.example.demo.config包中新建JedisConfig类,将配置信息注入这个配置类的bean,关键代码如下所示:

@Component
@ConfigurationProperties(prefix = "redis")
public class JedisConfig {

    private String host;

    private int port;

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

注意,@ConfigurationProperties注解指定了配置项的前缀为redis,它和这个类的实例变量拼接成具体的配置项名称,也就是redis.hostredis.port

6. 监听和处理配置更新事件

当在Apollo服务端修改配置时,Apollo客户端可以监听ConfigChangeEvent事件,然后处理这个配置更新事件。在示例工程中,新建com.example.demo.callback包,在该包中新建RedisServiceUpdateCallback类,关键代码如下所示:

@Component
public class RedisServiceUpdateCallback {

    protected static final Logger LOGGER = LoggerFactory.getLogger(RedisServiceUpdateCallback.class);

     @Autowired
     private RedisService redisService;

     @ApolloConfig("redis")
     private Config redisConfig;

    @ApolloConfigChangeListener({ "redis" })
    private void onRedisChange(ConfigChangeEvent changeEvent) {
         String host = redisConfig.getProperty("redis.host", "10.15.1.21");
         int port = redisConfig.getIntProperty("redis.port", 6379);
         redisService.changeJedis(host, port);
    }
}

上述代码,有三点需要注意:

  • @ApolloConfig注解:将Apollo服务端的redis命名空间(也就是redis.properties文件)中的配置注入这个类的bean实例。
  • @ApolloConfigChangeListener注解:监听redis命名空间的配置更新事件,若该事件发生,则调用onRedisChange方法,处理该事件。
  • ConfigChangeEvent参数:虽然示例代码没有使用这个参数,但是它很实用,可以获取被修改配置项的key集合,以及被修改配置项的新值、旧值和修改类型等信息。

7. 其他代码

其他功能代码,本文就不再一一赘述,可以使用Git下载本示例工程,Git路径如下所示:

http://10.15.1.248/yangbin/apollo-springboot-demo.git

这些功能代码的作用,如下所示:

  • JedisUtil:Jedis工具类,负责创建Redis连接。
  • RedisService:缓存服务类,负责向缓存写入/获取数据,以及连接配置更新时自动重连。
  • RedisController:缓存功能控制器类,负责接收操作请求,以及显示操作结果。
  • ApolloSpringbootDemoApplication:示例工程的启动类。

三、创建和配置Apollo项目

1. 登录Apollo系统

在Web浏览器中,打开Apollo服务端的URL:

http://10.15.1.35:8070/

输入默认的用户名和密码(分别是apollo和admin),登录Apollo系统,首页如下图所示:

登录Apollo系统

2. 创建项目

点击“创建项目”按钮,跳转至Apollo配置中心的创建项目页面,填写各项信息,如下图所示:

创建新项目

输入的项目信息,如下所示:

  • 部门:选择应用所在的部门。
  • 应用Id:用来标识应用身份的唯一标识符,格式为字符串,需要和客户端app.properties中配置的app.id对应。
  • 应用名称:应用名,仅用于界面展示。
  • 应用负责人:选择的人默认会成为该项目的管理员,具备项目权限管理、集群创建、Namespace创建等权限。

项目创建成功之后,会自动跳转至项目首页,如下图所示:

Demo项目的首页

注意,新项目默认会自带application命名空间(也就是application.properties文件)。

3. 添加应用服务端口配置项

在项目首页中,点击application命名空间的“新增配置”按钮,如下图所示:

向application命名空间新增配置项

然后,弹出添加配置项页面,填写各项信息,如下图所示:

新增服务器端口号的配置项

最后,点击“提交”按钮,返回至项目首页,此时能够看到server.port配置项,如下图所示:

显示application命名空间的配置项列表

从上图可知,server.port配置项尚未发布,这样的配置项暂时还不能使用,稍后会发布新建的配置项。

4. 创建redis命名空间

在项目首页,点击左下方的“添加Namespace”按钮,跳转至新建Namespace页面,然后再点击“创建Namespace”按钮,填写各项信息,如下图所示:

新建redis命名空间

点击“提交”按钮,保存redis命名空间,然后跳转至权限管理页面,添加修改权限和发布权限,如下图所示:

配置项的修改和发布权限

点击“返回到项目首页”按钮,返回至项目首页,如下图所示:

再次返回项目首页

在项目首页中,点击redis命名空间的“新增配置”按钮,然后按照添加server.port配置项的步骤,添加redis.hostredis.port配置项。redis命名空间,如下图所示:

显示redis命名空间的配置项列表

从上图可知,redis.hostredis.port配置项尚未发布,这样的配置项暂时还不能使用,稍后会发布新建的配置项。

5. 发布命名空间

Apollo将配置项的修改权限和发布权限分离,可以指定给不同的用户,这样就相当于引入了配置修改评审的机制,可以有效避免误修改的问题。

新建和修改配置项之后,还需要对外发布才能使用。在项目首页中,分别点击application和redis命名空间中的“发布”按钮,如下图所示:

发布application和redis命名空间的配置项

发布完成之后,可以看到每个配置项的发布状态都会变成“已发布”状态,此时配置项就正式生效了!

四、验证测试

1. 启动示例工程

在STS中,配置示例工程的VM启动参数,如下图所示:

在STS中修改VM参数

此处,设置了两个参数:

  • -Denv=dev
    表示连接至Apollo的DEV环境。

  • -Ddev_meta=http://10.15.1.35:8080
    指定DEV环境的Apollo服务器的IP地址。

然后,点击“Run”按钮,启动示例工程。启动完成之后,可以在Apollo的项目首页中,看到两个命名空间的实例列表,如下图所示:

每个命名空间的实例列表

点击redis命名空间的“实例列表”标签,可以看到使用这个命名空间的主机实例,如下图所示:

redis命名空间的实例信息

实例列表要注意以下几点:

  • IP列中的信息是实例主机的第一块非回环网卡的IP地址。
  • Data Center列中的信息取自于实例主机的/opt/settings/server.properties文件中的idc配置项,本文没有设置。
  • 实例列表不能实时显示连接的实例主机信息,只能显示最近一天访问过的实例主机。

2. 添加测试数据

在实例-1的Redis缓存中,添加以下K-V对:

"test_key":"XXL-JOB, HelloWorld from redis 01 ..."

在实例-2的Redis缓存中,添加以下K-V对:

"test_key":"XXL-JOB, HelloWorld from redis 02 ..."

示例工程一开始连接至实例-1的Redis缓存。

3. 访问测试数据

在Web浏览器中访问以下URL:

http://127.0.0.1:8888/get.do?key=test_key

若浏览器显示的信息如下图所示,则表示示例工程成功连接至实例-1的Redis缓存:

获取第一个Redis中的测试数据

4. 修改Redis连接配置

在Apollo的项目首页中,将redis命名空间的redis.host配置项的取值修改成实例-2的Redis缓存的地址,也就是192.168.190.128,然后发布更新的配置项,如下图所示:

修改Redis连接配置

发布配置项之后,示例工程会监听到配置更新,然后重新连接至实例-2的Redis缓存,STS控制的日志如下图所示:

配置更新监听器的日志

5. 再次访问测试数据

在Web浏览器中再次访问以下URL:

http://127.0.0.1:8888/get.do?key=test_key

若浏览器显示的信息如下图所示,则表示示例工程成功连接至实例-2的Redis缓存,实现了Redis连接配置的热更新和自动重连:

从第二个Redis获取测试数据

五、结语

本文通过示例工程介绍了Apollo的两个最主要的功能,如下所示:

  • 配置文件托管,包括BootStrap阶段和PostProcessBeanFactory阶段。
  • 监听和处理配置更新事件。

除此之外,还有一些高级功能需要花费时间尝试和探索,它们能够进一步提高日常开发和运维/运营的效率:

  • 应用内划分集群
  • 灰度发布
  • 配置更新审计(配置发布、回滚和发布历史)
  • 多环境同步配置