Spring Boot 集成 Apollo 配置中心,真香、真强大!

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
日志服务 SLS,月写入数据量 50GB 1个月
简介: 由于 Apollo 概念比较多,刚开始使用比较复杂,最好先过一遍概念再动手实践尝试使用。

由于 Apollo 概念比较多,刚开始使用比较复杂,最好先过一遍概念再动手实践尝试使用。


1、背景

随着程序功能的日益复杂,程序的配置日益增多,各种功能的开关、参数的配置、服务器的地址……对程序配置的期望值也越来越高,配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制……


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


2、简介

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


3、特点

部署简单

灰度发布

版本发布管理

提供开放平台API

客户端配置信息监控

提供Java和.Net原生客户端

配置修改实时生效(热发布)

权限管理、发布审核、操作审计

统一管理不同环境、不同集群的配置

4、基础模型

如下即是 Apollo 的基础模型:


(1)、用户在配置中心对配置进行修改并发布

(2)、配置中心通知Apollo客户端有配置更新

(3)、Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用

image.png

5、Apollo 的四个维度

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


application (应用)

environment (环境)

cluster (集群)

namespace (命名空间)

(1)、application


Apollo 客户端在运行时需要知道当前应用是谁,从而可以根据不同的应用来获取对应应用的配置。

每个应用都需要有唯一的身份标识,可以在代码中配置 app.id 参数来标识当前应用,Apollo 会根据此指来辨别当前应用。

(2)、environment


在实际开发中,我们的应用经常要部署在不同的环境中,一般情况下分为开发、测试、生产等等不同环境,不同环境中的配置也是不同的,在 Apollo 中默认提供了四种环境:


FAT(Feature Acceptance Test):功能测试环境

UAT(User Acceptance Test):集成测试环境

DEV(Develop):开发环境

PRO(Produce):生产环境

在程序中如果想指定使用哪个环境,可以配置变量 env 的值为对应环境名称即可。


(3)、cluster


一个应用下不同实例的分组,比如典型的可以按照数据中心分,把上海机房的应用实例分为一个集群,把北京机房的应用实例分为另一个集群。

对不同的集群,同一个配置可以有不一样的值,比如说上面所指的两个北京、上海两个机房设置两个集群,两个集群中都有 mysql 配置参数,其中参数中配置的地址是不一样的。

(4)、namespace


一个应用中不同配置的分组,可以简单地把 namespace 类比为不同的配置文件,不同类型的配置存放在不同的文件中,如数据库配置文件,RPC 配置文件,应用自身的配置文件等。


熟悉 SpringBoot 的都知道,SpringBoot 项目都有一个默认配置文件 application.yml,如果还想用多个配置,可以创建多个配置文件来存放不同的配置信息,通过指定 spring.profiles.active 参数指定应用不同的配置文件。这里的 namespace 概念与其类似,将不同的配置放到不同的配置 namespace 中。


Namespace 分为两种权限,分别为:


public(公共的): public权限的 Namespace,能被任何应用获取。

private(私有的): 只能被所属的应用获取到。一个应用尝试获取其它应用 private 的 Namespace,Apollo 会报 “404” 异常。

Namespace 分为三种类型,分别为:


私有类型: 私有类型的 Namespace 具有 private 权限。例如 application Namespace 为私有类型。

公共类型: 公共类型的 Namespace 具有 public 权限。公共类型的N amespace 相当于游离于应用之外的配置,且通过 Namespace 的名称去标识公共 Namespace,所以公共的 Namespace 的名称必须全局唯一。

关联类型(继承类型): 关联类型又可称为继承类型,关联类型具有 private 权限。关联类型的 Namespace 继承于公共类型的 Namespace,将里面的配置全部继承,并且可以用于覆盖公共 Namespace 的某些配置。

6、本地缓存

Apollo客户端会把从服务端获取到的配置在本地文件系统缓存一份,用于在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置,不影响应用正常运行。


本地缓存路径默认位于以下路径,所以请确保/opt/data或C:\opt\data\目录存在,且应用有读写权限。


Mac/Linux: /opt/data/{appId}/config-cache

Windows: C:\opt\data{appId}\config-cache

本地配置文件会以下面的文件名格式放置于本地缓存路径下:

image.png

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


客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。

客户端还会定时从 Apollo 配置中心服务端拉取应用的最新配置。

这是一个 fallback 机制,为了防止推送机制失效导致配置不更新

客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回 304 - Not Modified

定时频率默认为每 5 分钟拉取一次,客户端也可以通过在运行时指定 apollo.refreshInterval 来覆盖,单位为分钟。

客户端从 Apollo 配置中心服务端获取到应用的最新配置后,会保存在内存中。

客户端会把从服务端获取到的配置在本地文件系统缓存一份 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置。

应用程序从 Apollo 客户端获取最新的配置、订阅配置更新通知。

配置更新推送实现


前面提到了 Apollo 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。长连接实际上我们是通过 Http Long Polling 实现的,具体而言:


客户端发起一个 Http 请求到服务端

服务端会保持住这个连接 60 秒

如果在 60 秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的 namespace 信息,客户端会据此拉取对应 namespace 的最新配置

如果在 60 秒内没有客户端关心的配置变化,那么会返回 Http 状态码 304 给客户端

客户端在收到服务端请求后会立即重新发起连接,回到第一步

考虑到会有数万客户端向服务端发起长连,在服务端我们使用了 async servlet(Spring DeferredResult) 来服务 Http Long Polling 请求。

8、总体设计

image.png

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


Config Service 提供配置的读取、推送等功能,服务对象是 Apollo 客户端

Admin Service 提供配置的修改、发布等功能,服务对象是 Apollo Portal(管理界面)

Config Service 和 Admin Service 都是多实例、无状态部署,所以需要将自己注册到 Eureka 中并保持心跳

在 Eureka 之上我们架了一层 Meta Server 用于封装Eureka的服务发现接口

Client 通过域名访问 Meta Server 获取Config Service服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Client 侧会做 load balance 错误重试

Portal 通过域名访问 Meta Server 获取 Admin Service 服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Portal 侧会做 load balance、错误重试

为了简化部署,我们实际上会把 Config Service、Eureka 和 Meta Server 三个逻辑角色部署在同一个 JVM 进程中

9、可用性考虑

配置中心作为基础服务,可用性要求非常高,下面的表格描述了不同场景下Apollo的可用性:

image.png

Apollo 配置中心创建项目与配置

接下来我们将创建一个 Apollo 的客户端项目,引用 Apollo 来实现配置动态更新,不过在此之前我们需要提前进入 Apollo Portal 界面,在里面提前创建一个项目并在其配置一个参数,方便后续客户端引入该配置参数,测试是否能动态变化。


1、登录 Apollo

我这里是部署到 Kubernetes 中,通过 NodePort 方式暴露出一个端口,打开这个地址登录 Apollo:


用户名:apollo

密 码:admin

image.png

2、修改与增加部门数据

在登录后创建项目时,选择部门默认只能选择 Apollo 自带的 测试部门1与测试部门2两个选项。

image.png

开始这真让人迷糊,原来 Apoloo 没有修改或新增部门信息的管理节目,只能通过修改数据库,来新增或者修改数据,这里打开 Portal 对月的数据库中的表 ApolloPortalDB 修改 key 为 organizations 的 value 的 json 数据,改成自己对于的部门信息。

image.png

3、创建一个项目

修改完数据库部门信息后,重新登录 Apollo Portal,然后创建项目,这时候选择部门可以看到已经变成我们自己修改后的部门信息了,选择我们自定义部门,然后设置应用 ID 为 apollo-test,应用名为 apollo-demo

image.png

创建完成后进入配置管理界面:


image.png

4、创建一个配置参数

创建一个配置参数,方便后续 Apollo 客户端项目引入该参数,进行动态配置测试。

image.png

image.png

 1
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4    <modelVersion>4.0.0modelVersion>
 5
 6    <parent>
 7        <groupId>org.springframework.bootgroupId>
 8        <artifactId>spring-boot-starter-parentartifactId>
 9        <version>2.1.8.RELEASEversion>
10    parent>
11
12    <groupId>club.mydlqgroupId>
13    <artifactId>apollo-demoartifactId>
14    <version>0.0.1version>
15    <name>apollo-demoname>
16    <description>Apollo Demodescription>
17
18    <properties>
19        <java.version>1.8java.version>
20    properties>
21
22    <dependencies>
23        <dependency>
24            <groupId>org.springframework.bootgroupId>
25            <artifactId>spring-boot-starter-webartifactId>
26        dependency>
27        <dependency>
28            <groupId>com.ctrip.framework.apollogroupId>
29            <artifactId>apollo-clientartifactId>
30            <version>1.4.0version>
31        dependency>
32    dependencies>
33
34    <build>
35        <plugins>
36            <plugin>
37                <groupId>org.springframework.bootgroupId>
38                <artifactId>spring-boot-maven-pluginartifactId>
39            plugin>
40        plugins>
41    build>
42
43project>

2、配置文件添加参数

在 application.yml 配置文件中添加下面参数,这里简单介绍下 Apollo 参数作用:


apollo.meta: Apollo 配置中心地址。

apollo.cluster: 指定使用某个集群下的配置。

apollo.bootstrap.enabled: 是否开启 Apollo。

apollo.bootstrap.namespaces : 指定使用哪个 Namespace 的配置,默认 application。

apollo.cacheDir=/opt/data/some-cache-dir: 为了防止配置中心无法连接等问题,Apollo 会自动将配置本地缓存一份。

apollo.autoUpdateInjectedSpringProperties: Spring应用通常会使用 Placeholder 来注入配置,如${someKey:someDefaultValue},冒号前面的是 key,冒号后面的是默认值。如果想关闭 placeholder 在运行时自动更新功能,可以设置为 false。

apollo.bootstrap.eagerLoad.enabled : 将 Apollo 加载提到初始化日志系统之前,如果设置为 false,那么将打印出 Apollo 的日志信息,但是由于打印 Apollo 日志信息需要日志先启动,启动后无法对日志配置进行修改,所以 Apollo 不能管理应用的日志配置,如果设置为 true,那么 Apollo 可以管理日志的配置,但是不能打印出 Apollo 的日志信息。

image.png

3、创建测试 Controller 类

写一个 Controller 类来输出 test 变量的值,使用了 Spring 的 @Value 注解,用于读取配置文件中的变量的值,这里来测试该值,项目启动后读取到的变量的值是设置在 application 配置文件中的默认值,还是远程 Apollo 中的值,如果是 Apollo 中配置的值,那么再测试在 Apollo 配置中心中改变该变量的值后,这里是否会产生变化。

image.png

5、JVM 启动参数添加启动参数

由于本人的 Apollo 是部署在 Kubernetes 环境中的,JVM 参数中必须添加两个变量:


env: 应用使用 Apollo 哪个环境,例如设置为 DEV 就是指定使用开发环境,如果设置为 PRO 就是制定使用生产环境。

apollo.configService: 指定配置中心的地址,跳过 meta 的配置,在测试时指定 meta 地址无效果。如果 Apollo 是部署在 Kubernetes 中,则必须设置该参数为配置中心地址,如果 Apollo 不是在 Kubernetes 环境中,可以不设置此参数,只设置 meta 参数即可。一般情况下,configService 和 meta 值一致。

如果是在 Idea 中启动,可以配置启动参数,加上:


1-Dapollo.configService=http://192.168.2.11:30002 -Denv=DEV

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

(2)、两个集群都配置同样的参数不同的值

在两个集群 beijingshanghai 中,都统一配置参数 test,并且设置不同的值。

image.png

(3)、示例项目 application.yml 修改集群配置参数,并启动项目观察结果

指定集群为 beijing:

image.png

可以看到用的是 shanghai 集群的配置


3、不同命名空间下的配置

(1)、创建两个命名空间


命名空间有两种,一种是 public(公开),一种是 private 私有,公开命名空间所有项目都能读取配置信息,而私有的只能 app.id 值属于该应用的才能读取配置。


这里创建 dev-1 与 dev-2 两个私有的命名空间,用于测试。

image.png

image.png

image.png

(2)、两个集群都配置同样的参数不同的值

在两个命名空间中,都统一配置参数 test,并且设置不同的值,设置完后发布。

image.png

(3)、示例项目 application.yml 修改命名空间配置参数,并启动项目观察结果

指定命名空间为 dev-1:

1......
1......
2
3apollo:
4  bootstrap:
5    namespaces: dev-1                   #设置 dev-1 命名空间
6
7......

image.png

image.png

springboot-apollo.yaml


 1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: springboot-apollo
 5spec:
 6  type: NodePort
 7  ports:
 8    - name: server
 9      nodePort: 31080
10      port: 8080
11      targetPort: 8080
12    - name: management
13      nodePort: 31081
14      port: 8081
15      targetPort: 8081
16  selector:
17    app: springboot-apollo
18---
19apiVersion: apps/v1
20kind: Deployment
21metadata:
22  name: springboot-apollo
23  labels:
24    app: springboot-apollo
25spec:
26  replicas: 1
27  selector:
28    matchLabels:
29      app: springboot-apollo
30  template:
31    metadata:
32      name: springboot-apollo
33      labels:
34        app: springboot-apollo
35    spec:
36      restartPolicy: Always
37      containers:
38        - name: springboot-apollo
39          image: mydlqclub/springboot-apollo:0.0.1
40          imagePullPolicy: Always
41          ports:
42            - containerPort: 8080
43              name: server
44          env:
45            - name: JAVA_OPTS
46              value: "-Denv=DEV"
47              ##注意修改此处的 mydlqcloud 为你自己的 Namespace 名称
48            - name: APP_OPTS
49              value: "
50                     --app.id=apollo-demo
51                     --apollo.bootstrap.enabled=true
52                     --apollo.bootstrap.eagerLoad.enabled=false
53                     --apollo.cacheDir=/opt/data/
54                     --apollo.cluster=default
55                     --apollo.bootstrap.namespaces=application
56                     --apollo.autoUpdateInjectedSpringProperties=true
57                     --apollo.meta=http://service-apollo-config-server-dev.mydlqcloud:8080    
58                     "
59          resources:
60            limits:
61              memory: 1000Mi
62              cpu: 1000m
63            requests:
64              memory: 500Mi
65              cpu: 500m

(2)、部署 SpringBoot 应用到 Kubernetes


-n:创建应用到指定的 Namespace 中。


1$ kubectl apply -f springboot-apollo.yaml -n mydlqcloud

3、测试部署的应用接口

上面的应用配置了 NodePort 端口,可以通过此端口访问 Kubernetes 集群内的应用接口,本人 Kubernetes 集群地址为 192.168.2.11 且 NodePort 端口为 31081,所以浏览器访问地址 http://192.168.2.11:31081/test 来测试接口,显示:


1test的值为:123456

可以看到能通过 Apollo 获取参数值,此文章到此结束。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
7天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
27 0
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
36 0
|
12天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
24 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
10天前
|
Java 数据库连接
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
38 8
|
8天前
|
Java 数据库连接 数据库
springboot启动配置文件-bootstrap.yml常用基本配置
以上是一些常用的基本配置项,在实际应用中可能会根据需求有所变化。通过合理配置 `bootstrap.yml`文件,可以确保应用程序在启动阶段加载正确的配置,并顺利启动运行。
14 2
|
12天前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
47 1
|
19天前
|
Java Spring 容器
SpringBoot读取配置文件的6种方式,包括:通过Environment、@PropertySource、@ConfigurationProperties、@Value读取配置信息
SpringBoot读取配置文件的6种方式,包括:通过Environment、@PropertySource、@ConfigurationProperties、@Value读取配置信息
43 3
|
22天前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
24 1
|
30天前
|
druid Java Maven