尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Apollo配置中心接入Spring Boot实战:动态刷新与灰度发布

Apollo配置中心接入Spring Boot实战:动态刷新与灰度发布 春节前我参与一个数据可视化大屏项目时核心指标突然全部显示为 0排查了一整天才发现是某个同事在本地配置文件中临时改了数据库地址结果不小心提交到了测试环境。那一次之后我彻底意识到配置不能散落在各个微服务的代码里必须集中管理、权限可控、变更可追溯。后来我整理了这套 Apollo 配置中心接入 Spring Boot 的完整实战笔记包含动态刷新、灰度发布思路、常见报错解决方案适合刚接触配置中心的同学也适合准备在项目中落地的后端开发。1. 为什么需要 Apollo 配置中心在传统单体应用中配置通常写在application.properties或application.yml里改配置就要改代码、重新打包、重启服务。到了微服务架构下服务数量变多环境也变多开发、测试、预发、生产这种方式的弊端就被放大了。Apollo 是什么Apollo阿波罗是携程框架部门开源的分布式配置中心能够集中管理不同环境、不同集群的配置。配置修改后可以实时推送到应用端并且具备版本管理、灰度发布、权限控制、审计等企业级特性。通俗一点理解Apollo 就是一个“配置的中央仓库”你的应用启动时来这里拿配置。配置修改后应用不用重启就能拿到新值。谁能改配置、谁改了配置都有记录。不想冒险全量生效可以先灰度给少量实例。解决了什么痛点配置中心真正解决的是几个核心问题配置与代码分离。同一个代码包部署到不同环境不需要重新构建只需要在配置中心为每个环境维护一套配置。实时生效。数据库连接池大小、开关类配置、限流阈值等变更不再需要重启应用。集中管控。配置不再散落在各个开发者的本地文件里而是集中在一个平台权限控制更清晰。变更可追溯。配置何时被谁修改、修改前后的值是什么都有记录。线上出了问题可以快速回溯。适合哪些场景如果你遇到以下情况配置中心的价值会非常明显微服务数量超过 10 个手工维护配置已经力不从心。多个环境之间配置差异大经常出现“测试环境好的生产环境不行”。线上问题需要临时调整开关或阈值但不能接受重启应用。团队人数较多需要防止有人误改生产配置。需要配置灰度发布先让部分实例验证新配置。当然如果只是一个简单的单体应用配置项不超过 20 个也没有多环境部署需求传统配置文件完全够用。引入配置中心反而会增加维护成本。2. 环境准备与版本说明Apollo 的版本迭代比较快不同版本在配置项和管理界面细节上会有差异但核心流程是相通的。本文以一套常见且稳定的组合为例来说明。2.1 本地环境演示环境如下操作系统Windows 10 / macOS / Linux 均可。JDK1.8 或更高版本。Maven3.6。Apollo 服务端1.9.1。Apollo 客户端1.9.1。Spring Boot2.5.4。IDEIDEA。数据库MySQL 5.7。如果你的项目使用 Spring Boot 2.3.x 或 3.x版本对应关系可能略有不同但整体集成思路一致。下文涉及版本的地方我会提醒你按实际环境调整。2.2 了解 Apollo 服务端的三个角色很多初学者第一次接触 Apollo 时会被它的一堆服务绕晕。简单梳理一下Config Service为客户端提供配置读取、推送功能默认端口 8080。Admin Service为管理界面 Portal 提供配置管理功能默认端口 8090。Portal管理界面负责配置的修改、发布、权限管理默认端口 8070。如果使用官方 Quick Start脚本会一次性把这三个服务全部启动。2.3 快速启动 Apollo 服务端这里提供两种常见方式。方式一使用官方 Quick Start。官方提供的apollo-quick-start压缩包会自带 MySQL 初始化脚本并启动三个服务。下载解压后执行启动脚本即可。Windows 下双击startup.cmdLinux/macOS 下执行./startup.sh启动完成后访问http://localhost:8070即可打开 Apollo Portal。方式二使用 Docker Compose。如果本地已经安装 Docker也可以编写docker-compose.yml启动。下面给出参考配置version: 3 services: apollo-configservice: image: apolloconfig/apollo-configservice:1.9.1 container_name: apollo-configservice ports: - 8080:8080 environment: - EUREKA_INSTANCE_HOME_PREFER_IP_ADDRESStrue - SPRING_DATASOURCE_URLjdbc:mysql://host.docker.internal:3306/ApolloConfigDB?characterEncodingutf8 - SPRING_DATASOURCE_USERNAMEroot - SPRING_DATASOURCE_PASSWORD123456 depends_on: - apollo-db apollo-adminservice: image: apolloconfig/apollo-adminservice:1.9.1 container_name: apollo-adminservice ports: - 8090:8090 environment: - EUREKA_INSTANCE_HOME_PREFER_IP_ADDRESStrue - SPRING_DATASOURCE_URLjdbc:mysql://host.docker.internal:3306/ApolloConfigDB?characterEncodingutf8 - SPRING_DATASOURCE_USERNAMEroot - SPRING_DATASOURCE_PASSWORD123456 depends_on: - apollo-configservice apollo-portal: image: apolloconfig/apollo-portal:1.9.1 container_name: apollo-portal ports: - 8070:8070 environment: - APOLLO_PORTAL_ENVSdev - DEV_METAhttp://localhost:8080 depends_on: - apollo-configservice - apollo-adminserviceDocker 方式需要根据本机 MySQL 实际地址和账号密码调整连接信息。如果这是你第一次接触 Apollo建议先用 Quick Start 把核心链路跑通再考虑容器化部署。2.4 创建示例项目新建一个 Spring Boot 项目项目结构如下apollo-demo ├── pom.xml ├── src │ └── main │ ├── java │ │ └── com │ │ └── example │ │ └── apollo │ │ ├── ApolloDemoApplication.java │ │ ├── config │ │ │ └── ApolloConfig.java │ │ └── controller │ │ └── ConfigController.java │ └── resources │ ├── application.yml │ └── META-INF │ └── app.properties └── targetpom.xml中引入 Apollo 客户端依赖和 Spring Boot 相关依赖parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.5.4/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.ctrip.framework.apollo/groupId artifactIdapollo-client/artifactId version1.9.1/version /dependency /dependencies如果使用 Spring Boot 3.x需要确认 Apollo 客户端版本是否已经适配版本不匹配时会出现初始化异常。3. Apollo 与 Spring Boot 集成的核心配置配置中心不是一个“加依赖就能用”的东西它需要应用在启动阶段完成 Apollo 客户端的初始化、配置拉取、注入 Spring 容器以及在运行时监听配置变更。下面按步骤拆解。3.1 配置 app.propertiesApollo 客户端启动时需要先确定两件事当前应用是谁对应 Apollo 里的 AppId。Apollo 配置中心在哪里对应 Meta Server 地址。这两个信息通常放在resources/META-INF/app.properties中。# 文件路径src/main/resources/META-INF/app.properties app.idapollo-demo apollo.metahttp://localhost:8080这里有几个容易踩的坑AppId必须和 Apollo Portal 中创建的应用一致不一致时客户端会提示找不到配置。apollo.meta是 Meta Server 地址不是 Portal 地址。Apollo 的 Portal 默认端口是 8070Config Service 默认端口是 8080两者不能混用。如果系统环境变量里已经设置了APOLLO_META它的优先级会高于配置文件这可能导致“明明配置了却无效”的诡异问题。另外实际部署时更推荐在启动命令中传入环境信息-DenvDEV或者通过环境变量管理 Meta Server 地址这样可以针对不同环境启动不同的配置。3.2 添加 Apollo 自动配置在 Spring Boot 项目中最常见的接入方式是在启动类上添加EnableApolloConfig注解。这个注解会触发 Apollo 的配置初始化逻辑把远程配置注入到 Spring 环境中。// 文件路径src/main/java/com/example/apollo/ApolloDemoApplication.java package com.example.apollo; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication EnableApolloConfig public class ApolloDemoApplication { public static void main(String[] args) { SpringApplication.run(ApolloDemoApplication.class, args); } }默认情况下EnableApolloConfig读取 Apollo 的application命名空间。如果配置在自定义命名空间需要加参数指定EnableApolloConfig(custom-namespace)注意命名空间名称一般是“应用名 格式后缀”的组合具体以 Apollo Portal 上的配置为准。命名空间对应不上时配置会静默读取不到不会启动报错这一点非常容易让人疑惑。3.3 配置 application.yml在 Spring Boot 中application.yml通常保留本地默认配置Apollo 上的配置项会覆盖本地配置项。# 文件路径src/main/resources/application.yml server: port: 8081 app: name: apollo-demo welcome: default-welcome这里app.welcome是本地默认值。Apollo 上如果配置了同名键启动时会用 Apollo 的值覆盖它。需要明确的是Apollo 配置的优先级高于本地配置文件。这意味着即使本地写了默认值只要 Apollo 存在同名配置最终生效的就是 Apollo 的值。3.4 使用 Value 注入配置业务代码中最常见的用法是使用Value读取配置项。// 文件路径src/main/java/com/example/apollo/controller/ConfigController.java package com.example.apollo.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class ConfigController { Value(${app.welcome:default}) private String welcome; GetMapping(/welcome) public String welcome() { return welcome; } }启动应用后访问http://localhost:8081/welcome返回的就是 Apollo 上配置的值。这里要提前说明一个关键点Value注入配置发生在 Bean 初始化阶段。如果不在代码中做额外处理Apollo 上的配置修改后这个字段不会自动更新。要实现动态刷新必须配合配置变更监听器。3.5 实现动态刷新Apollo 的动态刷新机制离不开两个要素ConfigChangeListener感知 Apollo 侧配置变化。EnvironmentChangeEvent通知 Spring 环境刷新配置绑定。下面是一个较为完整的监听器实现// 文件路径src/main/java/com/example/apollo/config/ApolloConfig.java package com.example.apollo.config; import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; Component public class ApolloConfig { Autowired private ApplicationEventPublisher eventPublisher; ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { for (String key : changeEvent.changedKeys()) { ConfigChange change changeEvent.getChange(key); System.out.println(String.format(配置变更key%s, oldValue%s, newValue%s, change.getPropertyName(), change.getOldValue(), change.getNewValue())); } // 发布 Spring 环境变更事件触发 Value 刷新 eventPublisher.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); } }这里的关键逻辑是Apollo 监听器感知到配置变化后发布 Spring 的EnvironmentChangeEvent。Spring 收到该事件后会重新解析配置并更新被Value注解标记的字段。如果不发布这个事件Apollo 侧配置已经变了但应用内存中的值不会自动更新。这是很多人测试动态刷新不生效时最常见的原因。如果你使用的项目中没有引入 Spring Cloud 相关依赖EnvironmentChangeEvent可能不存在。此时可以在监听器中手动更新字段但这种方式适合配置项较少的场景维护性会差一些。4. 完整实战从 Apollo 配置中心读取并动态刷新配置现在我们把前面讲到的知识点串起来完成一个完整的案例包含在 Apollo Portal 创建应用和命名空间。添加配置项并发布。Spring Boot 应用读取配置。通过监听器实现动态刷新。验证修改配置后接口返回新值。4.1 在 Apollo Portal 创建应用启动 Apollo 服务端后打开http://localhost:8070使用默认账号登录默认账号是apollo密码是admin。登录后点击“创建应用”填写应用 AppIdapollo-demo必须和本地app.properties中的app.id一致。应用名称可以随意填写比如“Apollo 示例应用”。部门选择已有的测试部门或者创建一个测试部门。创建完成后进入应用页面默认会有application命名空间。4.2 添加配置项在application命名空间中点击“新增配置”添加一个键值对Keyapp.welcomeValuehello from apollo保存后点击“发布”按钮。Apollo 的配置修改和发布是两个动作保存后配置处于“未发布”状态只有点击发布后客户端才能读取到新值。发布成功后配置列表里会显示发布日期和发布人。这里补充一个理解Apollo 的“发布”过程会生成一个新的 Release。客户端通过长连接或定时拉取的方式感知到 Release 变化然后更新本地缓存。4.3 启动 Spring Boot 应用启动项目前确认app.properties中apollo.meta的地址指向你的 Config Service。例如本地 Quick Start 环境下默认是app.idapollo-demo apollo.metahttp://localhost:8080在 IDEA 中直接启动ApolloDemoApplication。启动后控制台会出现类似下面的日志Apollo config loaded. AppId: apollo-demo, meta: http://localhost:8080这说明客户端已经成功连接 Apollo。如果日志中出现了Config service not found或连接超时优先检查 Meta Server 地址是否可达。4.4 验证静态配置读取访问http://localhost:8081/welcome。如果 Apollo 配置正常生效接口返回hello from apollo如果返回的是default或者本地默认值说明 Apollo 配置没有被读取需要检查app.id是否和 Apollo Portal 一致。apollo.meta是否指向 Config Service。Apollo 配置是否已经发布。项目中是否添加了EnableApolloConfig。4.5 验证动态刷新保持应用运行回到 Apollo Portal将app.welcome的值改为hello from apollo v2点击发布。随后观察应用控制台应该能看到监听器输出的日志配置变更keyapp.welcome, oldValuehello from apollo, newValuehello from apollo v2然后再次访问http://localhost:8081/welcome返回值应该已经变为hello from apollo v2如果返回值没有变化可以按下面的顺序排查代码中是否添加了ApolloConfigChangeListener。监听器中是否发布了EnvironmentChangeEvent。接口方法读取的字段是否被Value正确注入。监听器监听的命名空间和配置项所在命名空间是否一致。4.6 测试多配置项同时刷新一个命名空间下通常会有多个配置项。Apollo 的监听器会一次回调所有变化项你可以通过changedKeys()获取所有变更 key。ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { System.out.println(本次变更的配置项数量 changeEvent.changedKeys().size()); for (String key : changeEvent.changedKeys()) { System.out.println(变更 key key); } }这个能力在应对批量配置调整时很有用比如同时调整数据库连接池大小和超时时间一次发布即可。5. 常见问题与排查思路Apollo 集成的报错场景很多下面把高频问题整理成表格并对几个重点问题做详细展开。问题现象常见原因解决思路应用启动后没有拉取 Apollo 配置app.id与 Apollo 应用不一致核对app.properties与 Apollo Portal 应用信息日志报 Meta Server 连接失败apollo.meta地址写错或 Config Service 未启动先确认端口可达再查配置Value字段一直返回本地默认值命名空间不对或配置项未发布在 Apollo Portal 确认配置项是否发布修改 Apollo 配置后应用不刷新缺少监听器或缺少EnvironmentChangeEvent补全监听器和事件发布逻辑动态刷新时报类型转换异常新旧配置类型不一致检查配置类型和 Java 字段类型是否匹配多个环境配置串了环境变量APOLLO_META指向了错误环境检查环境变量和启动参数生产环境敏感配置泄露数据库密码等直接明文存储使用加密插件或应用侧解密5.1 启动时一直报 Meta Server 连接失败这个问题的根本原因通常是apollo.meta地址写错了。注意区分Portal 地址默认http://localhost:8070用于管理界面。Config Service 地址默认http://localhost:8080是客户端连接用的 Meta Server。如果启动时看到Could not find config for namespace或连接超时日志按下面顺序排查浏览器访问http://localhost:8080确认 Config Service 已启动。检查app.properties是否放在resources/META-INF目录下。检查app.id是否和 Portal 上一致。检查本地是否有APOLLO_META环境变量该变量会覆盖配置文件里的值。检查网络连通性有些公司内网环境需要配置 HTTP 代理。5.2 配置修改后没有动态刷新这个问题的排查路径比较固定。先确认监听器是否触发再确认事件是否发布。如果你在自定义命名空间里修改配置监听的注解要加命名空间参数ApolloConfigChangeListener(custom-namespace) public void onChange(ConfigChangeEvent changeEvent) { // ... }另外Value注解的字段默认只初始化一次。如果没有发布EnvironmentChangeEvent即使 Apollo 配置变了字段值也不会更新。这也是“监听器日志打印了但接口返回值不变”的原因。如果项目中没有引入spring-cloud-context可以引入dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-context/artifactId version3.0.3/version /dependency或者直接在监听器中手动更新字段ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { if (changeEvent.isChanged(app.welcome)) { this.welcome changeEvent.getChange(app.welcome).getNewValue(); } }这种方式仅适合配置项极少、没有引入 Spring Cloud 依赖的场景配置项一多就会很难维护。5.3 多个环境配置混乱Apollo 通过环境DEV、FAT、UAT、PRO隔离配置。本地调试时如果操作系统环境变量里设置了错误的APOLLO_META应用会连接到错误的环境。推荐在启动脚本中显式指定环境-DenvDEV或者在app.properties中配置envDEV生产环境部署时建议使用 Kubernetes ConfigMap 或环境变量方式注入 Meta 地址避免把环境信息写死在代码仓库中。6. 最佳实践与工程建议配置中心解决的是“配置管理”的问题。如果只是把application.yml里的内容原样搬到 Apollo那只是少了一次重启并没有真正发挥配置中心的价值。实务中我更推荐以下做法。6.1 合理划分命名空间不要把所有配置都塞进application命名空间。建议按业务模块或配置类型拆分application应用级通用配置如应用名、环境标识。datasource数据源配置。redisRedis 配置。business.order订单业务模块配置。security安全相关配置。拆分的好处是权限可以按命名空间隔离不同团队只管理自己的配置。同时配置的变更影响范围也更清晰不会出现“改一个配置影响全部模块”的情况。6.2 敏感信息加密处理Apollo 中不建议明文存储数据库密码、第三方密钥等敏感信息。常见的做法有两种第一种使用 Apollo 的加密插件。项目方可以基于 Apollo 的开放接口自行实现脱敏和加密能力。第二种在应用初始化阶段解密。流程为Apollo 上存储加密后的密文。应用读取密文后使用本地密钥解密。密钥存放在环境变量或 KMS 服务中不写入代码库。这样即便配置中心被拖库敏感配置也不会直接泄露。6.3 配置发布遵循灰度原则Apollo 的“发布”按钮影响的是整个命名空间下所有应用实例。如果某个配置项会触发线程池重建、连接池重建等高风险操作建议使用灰度发布。Apollo 支持按 IP 或按应用标签进行灰度发布。生产环境操作时建议遵循先在测试环境修改并发布配置。观察日志和监控指标确认配置变更生效。再在生产环境灰度给部分实例。灰度实例稳定后再全量发布。这里还要强调一点配置变更属于生产环境变更操作前务必确认变更内容、影响范围和回滚方案。Apollo 支持一键回滚到历史版本但线上故障时回滚前先评估业务影响会更稳妥。6.4 善用配置监听器做优雅刷新动态刷新不只是让Value字段值变化。像数据库连接池、线程池这类组件配置变更后需要重建实例才能生效。建议在监听器中做更细腻的处理ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { if (changeEvent.isChanged(db.pool.size)) { // 重建数据库连接池 } if (changeEvent.isChanged(thread.pool.size)) { // 更新线程池参数 } if (changeEvent.isChanged(feature.switch)) { // 更新业务开关 } }这样既保证了配置的灵活性又不会因每次配置变更都重启应用造成抖动。6.5 记录配置变更日志Apollo 本身有配置变更审计但建议在应用侧也记录一份变更日志。在监听器中打印变更前后值、变更时间、应用实例 IP 等信息。配置异常导致业务故障时这些日志能精准定位是哪一次配置变更引发的问题。ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { for (String key : changeEvent.changedKeys()) { ConfigChange change changeEvent.getChange(key); log.warn(配置变更key{}, oldValue{}, newValue{}, instanceIp{}, key, change.getOldValue(), change.getNewValue(), InetAddress.getLocalHost().getHostAddress()); } }6.6 使用 ConfigurationProperties 管理配置组当配置项较多时Value会显得零散。推荐使用ConfigurationProperties将一组相关配置绑定到配置类中。// 文件路径src/main/java/com/example/apollo/config/OrderProperties.java package com.example.apollo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; Component ConfigurationProperties(prefix order) public class OrderProperties { private int timeout; private int maxRetry; public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout timeout; } public int getMaxRetry() { return maxRetry; } public void setMaxRetry(int maxRetry) { this.maxRetry maxRetry; } }使用配置类的好处是结构清晰、易于校验同时配合 Apollo 动态刷新也能生效。需要说明的是ConfigurationProperties的类需要具备 setter 方法Spring 才能完成属性绑定。6.7 配置变更的测试策略配置虽然不涉及代码逻辑但它直接影响业务行为。建议在测试计划中加入配置变更的测试用例比如修改开关类配置验证开关是否立即失效。修改数据库连接池参数验证连接池是否重建。修改超时时间验证下游调用超时行为是否变化。修改不存在的配置项验证应用是否会有异常。配置项的不当修改也可能引发线上故障所以不能认为“只改配置不重启”就一定安全。7. 总结与学习路线这一套 Apollo 集成教程核心是解决三个问题Spring Boot 如何从 Apollo 配置中心读取配置。Apollo 配置变更后如何在不重启应用的情况下动态刷新。生产环境中如何安全地进行配置变更。从技术层面来说你已经掌握了Apollo 服务端三个角色的基本概念。客户端app.properties、EnableApolloConfig、Value的组合使用方式。ApolloConfigChangeListener和EnvironmentChangeEvent的动态刷新机制。Apollo Portal 上的配置发布流程。常见连接失败、刷新不生效等问题的排查方法。如果你希望继续深入建议按以下路线学习先用 Quick Start 把 Apollo 搭建起来亲手在界面上创建应用、添加配置、发布配置。用本文的 Spring Boot 项目接入 Apollo验证读取和动态刷新。尝试把数据源配置、Redis 配置等真实业务配置放到 Apollo 中管理。理解命名空间、集群、环境的关系设计适合自己团队的配置结构。深入了解 Apollo 的灰度发布、权限模型和开放 API与公司的运维体系打通。将配置管理与监控告警联动比如配置变更后自动触发应用健康检查。配置中心解决的不只是“改配置不用重启”的问题它更大的价值在于让配置变更有迹可循、有权限可控、有灰度可退。实际项目中建议先从低频风险低的配置开始接入运行稳定后再逐步扩大管理范围。如果配置项长期不变化放在代码仓库里也完全没问题不必为了用配置中心而强行迁移。
返回列表