Spring CloudspringSpring Cloud

Spring Cloud 系列之 Apollo 配置中心(一)

2020-06-02  本文已影响0人  哈喽沃德先生

背景

随着程序功能的日益复杂,程序的配置日益增多:各种功能的开关、参数的配置、服务器的地址等等。

对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制等等。

在这样的大环境下,传统的通过配置文件、数据库等方式已经越来越无法满足开发人员对配置管理的需求。

Apollo 配置中心应运而生!Apollo - 一个可靠的配置管理系统。

Apollo 介绍

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

Apollo 支持 4 个维度管理 Key-Value 格式的配置:

  1. application (应用)
  2. environment (环境)
  3. cluster (集群)
  4. namespace (命名空间 Namespace 是配置项的集合,类似于一个配置文件的概念)

同时,Apollo 基于开源模式开发,开源地址:https://github.com/ctripcorp/apollo

官网文档:https://github.com/ctripcorp/apollo/wiki/Quick-Start

演示环境(Demo):

上图是Apollo配置中心中一个项目的配置首页

Apollo 核心概念

  1. application (应用)
    • 这个很好理解,就是实际使用配置的应用,Apollo客户端在运行时需要知道当前应用是谁,从而可以去获取对应的配置
    • 每个应用都需要有唯一的身份标识 -- appId,我们认为应用身份是跟着代码走的,所以需要在代码中配置,具体信息请参见Java客户端使用指南
  2. environment (环境)
    • 配置对应的环境,Apollo客户端在运行时需要知道当前应用处于哪个环境,从而可以去获取应用的配置
    • 我们认为环境和代码无关,同一份代码部署在不同的环境就应该能够获取到不同环境的配置
    • 所以环境默认是通过读取机器上的配置(server.properties中的env属性)指定的,不过为了开发方便,我们也支持运行时通过System Property等指定,具体信息请参见Java客户端使用指南
  3. cluster (集群)
    • 一个应用下不同实例的分组,比如典型的可以按照数据中心分,把上海机房的应用实例分为一个集群,把北京机房的应用实例分为另一个集群。
    • 对不同的cluster,同一个配置可以有不一样的值,如zookeeper地址。
    • 集群默认是通过读取机器上的配置(server.properties中的idc属性)指定的,不过也支持运行时通过System Property指定,具体信息请参见Java客户端使用指南
  4. namespace (命名空间)
    • 一个应用下不同配置的分组,可以简单地把namespace类比为文件,不同类型的配置存放在不同的文件中,如数据库配置文件,RPC配置文件,应用自身的配置文件等
    • 应用可以直接读取到公共组件的配置namespace,如DAL,RPC等
    • 应用也可以通过继承公共组件的配置namespace来对公共组件的配置做调整,如DAL的初始数据库连接数

Apollo 特性

Apollo 总体设计

官方文档:https://github.com/ctripcorp/apollo/wiki/Apollo配置中心设计

架构模块

上图简要描述了 Apollo 的总体设计,我们可以从下往上看:

1.3 各模块概要介绍

1.3.1 Config Service
1.3.2 Admin Service
1.3.3 Meta Server
1.3.4 Eureka
1.3.5 Portal
1.3.6 Client

服务端

上图简要描述了配置发布的大致过程:

  1. 用户在Portal操作配置发布
  2. Portal调用Admin Service的接口操作发布
  3. Admin Service发布配置后,发送ReleaseMessage给各个Config Service
  4. Config Service收到ReleaseMessage后,通知对应的客户端

客户端

上图简要描述了Apollo客户端的实现原理:

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

环境准备

点击链接观看:Apollo 搭建服务端视频(获取更多请关注公众号「哈喽沃德先生」)

Java

由于需要同时启动服务端和客户端,所以建议安装Java 1.8+。

MySQL

Apollo的表结构对timestamp使用了多个default声明,所以需要5.6.5以上版本。

下载Quick Start安装包

Apollo 给我们准备好了一个Quick Start安装包,大家只需要下载到本地,就可以直接使用,免去了编译、打包过程。

安装包共50M,如果访问github网速不给力的话,可以从百度网盘下载。

  1. 从Github下载
  2. 从百度网盘下载
  3. 为啥安装包要58M这么大?
    • 因为这是一个可以自启动的jar包,里面包含了所有依赖jar包以及一个内置的tomcat容器

安装 Apollo

创建数据库

Apollo 服务端共需要两个数据库:ApolloPortalDBApolloConfigDB,我们把数据库、表的创建和样例数据都分别准备了 sql 文件,只需要导入数据库即可。

注意:如果你本地已经创建过Apollo数据库,请注意备份数据。我们准备的sql文件会清空Apollo相关的表。

创建 ApolloPortalDB 数据库

通过各种MySQL客户端导入sql/apolloportaldb.sql即可。

创建 ApolloConfigDB 数据库

通过各种MySQL客户端导入sql/apolloconfigdb.sql即可。

配置数据库连接信息

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

注意:填入的用户需要具备对 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=密码(如果没有密码,留空即可)

注意:不要修改 demo.sh 的其它部分

搭建服务端

确保端口未被占用

Quick Start脚本会在本地启动3个服务,分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用。

执行启动脚本

./demo.sh start

Apollo 提供的脚本文件为 .sh 文件,如果你的安装环境是在 Linux 系统下直接运行以上命令即可,如果你想在 Windows 环境下运行该脚本,先安装 Git 然后在 demo.sh 所在目录下鼠标右键点击 Git Bash Here,然后再通过以上命令运行脚本即可。

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

==== 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!

异常排查

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

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

访问

访问:http://localhost:8070/ Quick Start 集成了 Spring Security,输入用户名 apollo,密码 admin 后登录。

登录成功后,首页如下,Apollo 还提供了一个 SampleApp 样本案例供我们学习使用。

创建项目

点击对应按钮创建项目。

这里先通过默认的样例部门演示(后面我会讲如何添加部门),AppId 对应客户端配置文件中 app.id。

创建成功如下图。

客户端接入服务端

点击链接观看:Apollo 客户端接入服务端视频(获取更多请关注公众号「哈喽沃德先生」)

下面通过最常用、便捷的方式,即基于 Spring Boot 的集成方式来接入服务端。

apollo-demo 聚合工程。Spring Boot 2.2.4.RELEASE

添加依赖

<!-- https://mvnrepository.com/artifact/com.ctrip.framework.apollo/apollo-client -->
<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-client</artifactId>
    <version>1.6.0</version>
</dependency>

配置文件

order-serviceorder-service02 的配置信息除端口外一致。

server:
  port: 9090 # 端口

spring:
  application:
    name: order-service # 应用名称

# apollo 相关配置
app:
  id: order-service # 与 Apollo 配置中心中的 AppId 一致

apollo:
  meta: http://localhost:8080 # Apollo 中的 Eureka 注册中心地址
  #cluster:  # 指定 Apollo 集群,相同集群实例使用对应集群的配置
  #cacheDir:  # 配置缓存目录,网络不可用时任然可提供配置服务
  bootstrap:
    enable: true # 启用 apollo

env: DEV # 指定环境

# 自定义配置
name: order-service-dev
mysql:
  host: localhost
  port: 3306
  username: root
  password: root

配置文件实体类

order-serviceorder-service02 的配置文件实体类代码一致。

package com.example.config;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ConfigProperties {

    @Value("${name}")
    private String name;
    @Value("${mysql.host}")
    private String mysqlHost;
    @Value("${mysql.port}")
    private Integer mysqlPort;
    @Value("${mysql.username}")
    private String mysqlUsername;
    @Value("${mysql.password}")
    private String mysqlPassword;

}

控制层

order-serviceorder-service02 的控制层代码一致。

package com.example.controller;

import com.example.config.ConfigProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class ConfigController {

    @Autowired
    private ConfigProperties configProperties;

    @Value("${name}")
    private String name;

    @GetMapping("/name")
    public String getName() {
        return configProperties.getName();
    }

    @GetMapping("/mysql")
    public Map<Object, Object> getMySQLProperties() {
        // JDK9中的新特性,快速创建只读集合。
        return Map.of("host", configProperties.getMysqlHost(),
                "port", configProperties.getMysqlPort(),
                "username", configProperties.getMysqlUsername(),
                "password", configProperties.getMysqlPassword());
    }

}

启动类

启动类需要添加 @EnableApolloConfig 注解。

order-serviceorder-service02 的启动类代码一致。

package com.example;

import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableApolloConfig
@SpringBootApplication
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

}

测试

修改配置信息前

访问:http://localhost:9090/namehttp://localhost:9091/name 结果如下:

访问:http://localhost:9090/mysqlhttp://localhost:9091/mysql 结果如下:

新增配置信息

进入项目后点击右上角的 新增配置

添加配置项 namemysql.usernamemysql.password

发布配置信息

将刚才添加的配置信息批量发布至应用。

修改配置信息后

控制台打印信息如下:

c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: order-service-dev-2.0, key: name, beanName: configController, field: com.example.controller.ConfigController.name
c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: root123, key: mysql.password, beanName: configProperties, field: com.example.config.ConfigProperties.mysqlPassword
c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: root123, key: mysql.username, beanName: configProperties, field: com.example.config.ConfigProperties.mysqlUsername

访问:http://localhost:9090/namehttp://localhost:9091/name 结果如下:

访问:http://localhost:9090/mysqlhttp://localhost:9091/mysql 结果如下:

以上只是 Apollo 的入门教程,后面我们会学习 Apollo 的更多高级玩法,比如多环境部署,高可用环境搭建等等。

下一篇我们讲解 Apollo 部门管理、用户管理、配置管理、集群管理,记得关注噢~

本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议

大家可以通过 分类 查看更多关于 Spring Cloud 的文章。

🤗 您的点赞转发是对我最大的支持。

📢 关注公众号 哈喽沃德先生「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~

上一篇 下一篇

猜你喜欢

热点阅读