简介:Apollo 1.4.0 源码适配包面向需要将 Apollo 配置中心数据源从 MySQL 切换为 PostgreSQL 的 Java 开发与运维人员。官方默认仅支持 MySQL,该压缩包通过改动源码解除此限制,适配 PostgreSQL 11.4,并集成 42.2.5 版本 JDBC 驱动。资源共 2000 个文件,约 11.46MB,包含 646 个 Java 源文件、607 个编译后的 class 文件、64 个 SQL 脚本、61 个 XML 与 40 个 yml 配置,以及构建脚本、Dockerfile、前端静态资源等,便于直接查看改动点、编译部署或按需二次调整。.properties 与 yml 为主的配置模块和 SQL 初始化脚本均保持完整,可快速定位数据源相关的全部改动。内容预览中的 README 与 build.bat 提示包内保留构建说明和编译入口,ReleaseService 等核心类则有助于追踪服务端改造逻辑。目前已有 4184 人学习/下载,适合正在规划 Apollo 数据源扩展或需要落地 PostgreSQL 高可用方案的团队参考。
1. 拿到 apollo-1.4.0.zip 先别急着解压:先搞清它解决什么问题
一个微服务拆到十几个之后,配置管理就会变成最磨人的杂活。改一个开关要登服务器改文件再重启,开发、测试、生产三套环境各改各的,时间一长连自己都记不清哪台机器上的配置是新的。apollo-1.4.0.zip 这个压缩包,对应的就是一套开源的分布式配置中心,核心作用是把散落在各服务里的配置收拢到一个管理界面上,发布之后客户端自动感知、动态刷新,不用再为改配置重启服务。1.4.0 这个版本在配置中心里属于被验证得比较充分的一个版本,部署方式不复杂,单台机器就能跑完整套环境。这篇笔记适合正在搭微服务基座、想自己先跑通 Config Service、Admin Service、Portal 三个服务的团队,也适合手里只有这个 zip 包、想搞明白从解压到客户端接入完整链路的人。
2. 部署前准备:环境核对、数据库初始化、解压三件套
2.1 Java 环境与端口占用:两个容易忽略的前置条件
Apollo 的服务端是 Spring Boot 打的包,启动脚本本质上是 Java 进程,所以第一件事是确认机器上有 JDK。1.4.0 这个版本要求 JDK 8 及以上,我一般会用java -version先看一眼,确认不是只装了 JRE 就动手。很多部署卡在启动脚本报Unable to locate a Java Runtime这种错,根源就是 PATH 里没找到 java 或者 JAVA_HOME 指错了位置。
端口方面,默认端口是固定的:Config Service 是 8080,Admin Service 是 8090,Portal 是 8070。部署前用下面命令确认这些端口没被别的进程占用,我用的是 Golang 项目里常见的lsof,Linux 上也可以换ss -lntp。
lsof -i :8080 -i :8090 -i :8070提示:如果机器上同时跑着别的 Java 应用,8080 被占用的概率很高。不建议为了迁就端口去改系统里其他应用的配置,直接改 Apollo 的
server.port更干净,后面会讲到具体改哪几个文件。
内存方面,三个服务加起来大约需要 1.5GB 左右的堆内存,如果机器本身只有 2GB,建议只部署 Config Service 和 Portal,Admin Service 可以等用到管理功能时再起。这个取舍在初期验证阶段完全够用。
2.2 初始化数据库:两套 SQL 脚本的区别与执行顺序
服务端有三个模块,但数据库只需要两个库:ApolloPortalDB 和 ApolloConfigDB。PortalDB 存的是用户、权限、项目列表这类管理数据,ConfigDB 存的是真正的配置内容、发布历史、灰度规则。解压后 sql 目录下一般能看到两个脚本,分别对应这两个库。
一个常见的误操作是只导入了 ConfigDB,结果 Portal 能登录但看不到任何项目。两个库都要建,而且字符集建议直接指定utf8mb4,原因是配置内容里可能出现特殊字符或扩展字符集内容,默认的 latin1 会直接导致乱码。
mysql -u root -p -e "CREATE DATABASE ApolloPortalDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p -e "CREATE DATABASE ApolloConfigDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p ApolloPortalDB < apolloportaldb.sql mysql -u root -p ApolloConfigDB < apolloconfigdb.sql这里先建库再导数据,顺序不能反。如果先导数据再建库,MySQL 会直接报Unknown database。脚本执行完毕后,可以用SHOW TABLES;各看一眼,PortalDB 里常见的是users、permissions这类表,ConfigDB 里应该能看到app、item、release等表。看到这些表基本说明导入成功了。
2.3 解压后该认得哪些目录:配置文件的改动范围
zip 包解压后,常见做法是得到一个或多个按模块命名的目录,每个目录里都有conf和scripts两个关键目录。conf里放着application.properties和startup.sh,这两个文件就是要改的全部;scripts里是启动辅助脚本,正常不需要动。
我第一次部署时把所有文件都翻了一遍,后来发现真正需要改的就三类:数据源连接串、Eureka 地址、Portal 的访问地址。其他配置保持默认即可。这个认知能帮你省不少时间——不要被目录里大量配置文件干扰,Apollo 的默认行为已经适合大多数场景。
3. 单机部署三个服务:从配置修改到启动验证
3.1 修改 configservice 与 adminservice 的配置
先处理 Config Service。进入它的conf目录,打开application.properties,主要改数据源。默认文件里会有一段 Spring 数据源的配置,需要替换成你自己的 MySQL 地址、账号、密码。关键是要在连接串里带characterEncoding=utf8和serverTimezone,否则后面启动时可能报时区错误或者出现中文乱码。
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/ApolloConfigDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username = root spring.datasource.password = your_passwordConfig Service 内部嵌了 Eureka,所以它自己不需要额外指定注册中心地址,只要保证数据库能连上即可。Admin Service 的配置改动方式和它一样,只是连接的数据库同样是ApolloConfigDB,两个服务共享这一个库。这里有个细节:Admin Service 的application.properties里需要指定eureka.service.url指向 Config Service 的地��,默认值是http://127.0.0.1:8080/eureka/,单机部署不用改,但如果是多机部署,就要把127.0.0.1换成 Config Service 所在机器的实际 IP。
eureka.service.url = http://127.0.0.1:8080/eureka/注意:很多部署问题出在把 Config Service 和 Admin Service 的 IP 写成了
localhost,而客户端或 Portal 在另一台机器上解析不到。单机验证无所谓,一旦要给别人访问,统一用内网 IP。
3.2 配置 portal:数据源、地址、登录账号
Portal 是管理界面,配置项比前两个多一处。它的application.properties同样要改数据源,但连接的是ApolloPortalDB,而不是 ConfigDB。这一点最容易糊涂。另外还要配置 Admin Service 的访问地址,Portal 管理配置时实际是调用 Admin Service 的接口,所以这个地址错了会出现能登录但发布配置失败的情况。
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/ApolloPortalDB?characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username = root spring.datasource.password = your_password apollo.portal.envs = dev apollo.portal.meta.servers = {"dev":"http://127.0.0.1:8080"}apollo.portal.envs声明了当前环境列表,这里只写dev就够单机验证;如果你想模拟多环境,把dev和pro都写上,然后apollo.portal.meta.servers里对应给每个环境配置不同的 Config Service 地址。登录账号要自己往apollo-portal的conf目录下的apollo-env.properties里加,或者在数据库里直接插入一条用户记录,我一般图省事用后者。默认库里会有一个apollo用户,密码存在users表里,常见值是$2a$10$7fU7DVN0cJwkQzpB7pIqOuuASzqP8F6pGjVgJYvWQx6jzTZ1aTk6W这种 BCrypt 哈希,对应明文密码admin,注意这是一版很常见的默认组合,部署后第一件事就该改掉。
3.3 启动顺序与验证命令:三个端口必须各就各位
启动顺序我一般固定是 Config Service → Admin Service → Portal。原因不复杂:Config Service 内含 Eureka,Admin Service 启动时要向它注册,Portal 启动后要拉环境列表和 Admin Service 信息,链路是从下往上的。如果顺序反了,服务本身不会崩,但日志里全是连接超时,排查起来费神。
启动脚本都在各模块的scripts目录下,直接执行:
./startup.sh脚本内部会调java -jar,并把日志写到logs目录。启动后不要急着看界面,先用命令确认端口监听状态,每个服务启动大约要 10 到 20 秒,Eureka 注册可能需要更久。
ss -lntp | grep -E "8080|8090|8070"三个端口都出现在输出里,说明进程起来了。接着验证 Eureka 是否正常工作,Config Service 的 Eureka 接口可以直接用 curl 探:
curl http://127.0.0.1:8080/eureka/apps返回一段 XML,里面包含 Admin Service 和 Config Service 的注册信息,就说明注册中心正常。此时打开http://127.0.0.1:8070,能看到登录页,用默认账号登录后就是空的项目列表界面。到这里服务端就绪,可以开始接客户端了。
4. 客户端接入:Spring Boot 项目里配两个键、写一段读取逻辑
4.1 引入客户端依赖与版本对齐
服务端是 1.4.0,客户端依赖版本也要保持一致。在 Spring Boot 项目的pom.xml里加入 Apollo 客户端依赖,版本号直接写 1.4.0。很多人在这一步栽过跟头:客户端用了新版本或者旧版本,连上服务端后出现序列化不兼容或者接口 404。
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.4.0</version> </dependency>这个依赖会带入apollo-core和apollo-client-api,不需要额外手工添加。如果你的项目里同时有别的配置中心依赖,建议先排除掉或者确认接口不冲突,Apollo 的@Value注解在 Spring Boot 环境里会和原生注解冲突,后面会细说。
4.2 元数据配置:app.id 与 apollo.meta 的作用边界
客户端启动时要连到 Config Service 拿配置,它需要知道两件事:自己是谁、去哪找配置中心。前者是app.id,后者是apollo.meta。在application.yml里写:
app: id: sample-service apollo: meta: http://192.168.1.100:8080 bootstrap: enabled: true eagerLoad: enabled: trueapp.id对应 Portal 里创建项目时填的应用 ID,两边必须完全一致,大小写和特殊字符都要一字不差。apollo.meta填的是 Config Service 的地址,注意不是 Portal 的地址,也不是 Admin Service 的。eagerLoad.enabled建议设为true,否则配置是异步加载的,程序里第一次读取配置时可能拿到null,这个坑很隐蔽,尤其在 Spring Boot 启动阶段读取配置的场景里。
在 1.4.0 这个版本里,客户端拿到apollo.meta后会请求它的/eureka/apps接口获取 Config Service 实例列表,所以即使apollo.meta配的是内网 IP,也要保证客户端机器能访问到那个地址的 8080 端口。
4.3 两种读配置姿势:注解与程序化获取
接入 Apollo 后,代码里读配置的方式和原来差不多,但有一个关键区别需要注意。直接用@Value是最常见的姿势:
@Component public class CacheService { @Value("${redis.cache.ttl:300}") private int cacheTtl; public int getCacheTtl() { return cacheTtl; } }逻辑说明:@Value注解在 Spring 容器启动时注入属性值,${redis.cache.ttl:300}的意思是读 Apollo 上redis.cache.ttl这个配置项,如果不存在则用默认值 300。Apollo 的动态刷新能力在这里的体现是:当你在 Portal 上修改这个配置并发布后,Spring 容器里的cacheTtl字段会被自动更新,不需要重启服务。
参数说明:redis.cache.ttl这个 key 必须在 Apollo 配置里存在,否则走默认值;默认值冒号后面不能留空格。有一点容易翻车——如果你在类里同时用了@Value和自定义的 ConfigurationProperties 类读取同一份配置,刷新时前者会更新而后者不会,因为 Apollo 只对注解字段做注入。程序化获取是更可靠的方式:
Config config = ConfigService.getAppConfig(); Integer ttl = config.getIntProperty("redis.cache.ttl", 300);这种方式适合在非 Spring 管理的类里使用,或者需要动态获取多个配置项的场景。它每次都会实时取 Apollo 的本地缓存,配合监听器可以感知配置变更。两种姿势可以混用,但同一份配置别同时用两种方式维护,否则调试时不知道哪个值生效。
5. Apollo 1.4.0 部署避坑实录:五条现象与排查路径
5.1 configservice 启动日志一直报 Eureka 连接被拒
现象:Config Service 进程起来了,8080 端口也监听着,但日志每隔几秒刷一条Connection refused: connect,指向的地址是自己。
原因:Config Service 内嵌的 Eureka 在启动初期会尝试注册自己,而此时注册中心还没完全就绪,于是反复重试。另一个常见原因是你把eureka.service.url配置里的 localhost 写成了别的 IP,导致自行探测时连到了错误地址。
解决:如果日志里的地址是 127.0.0.1:8080,这属于正常启动过程,等 30 秒左右观察日志是否停止报错即可。如果持续报错,检查application.properties里eureka.instance.homePageUrl和eureka.service.url是否一致,全部改成机器实际内网 IP,然后重启。
5.2 portal 登录后项目列表一直空白
现象:登录 Portal 成功,但界面上看不到任何项目,手动创建项目时下拉框里也没有环境。
原因:最常见的是 Portal 连接的数据库不对,或者apollo.portal.envs和apollo.portal.meta.servers的配置有问题。Portal 启动后要请求 Config Service 获取环境信息,拿不到就会显示空白。
解决:先用 curl 确认 Config Service 的 Eureka 接口能返回服务列表,再检查 Portal 的application.properties里apollo.portal.meta.servers的 JSON 格式是否正确。这个配置项是字符串,不是 YAML 对象,常见错误是漏了外层引号或者写了单引号,导致解析失败。修正后重启 Portal。
5.3 客户端启动直接报 Apollo config not found
现象:Spring Boot 项目启动时抛异常,提示找不到 Apollo 配置,网上搜到的说法是配置地址连不上,但地址明明能 ping 通。
原因:apollo.meta配的是 Portal 地址而不是 Config Service 地址,或者app.id在 Portal 里还没创建对应的项目。前者会导致客户端请求到错误的端口,后者会让 Config Service 返回App not found。
解决:先确认 Portal 里已经创建了和app.id同名的项目。然后检查apollo.meta,这个地址一定要指向 Config Service 的 8080 端口。如果确认都正确,打开客户端的 debug 日志,在application.yml加一句logging.level.com.ctrip.framework.apollo=DEBUG,启动时会打印实际请求的 meta 地址和响应内容,问题瞬间定位。
5.4 配置改了但不生效:本地缓存与监听器两个疑点
现象:Portal 上修改配置并发布,客户端日志看不到拉取动作,代码里读取的值还是旧的,重启后才生效。
原因:Apollo 客户端默认有本地缓存,它会在启动后台线程定期拉取更新,但频率不是秒级的。另外如果代码里用了@ApolloConfigChangeListener但监听器内部逻辑有异常,刷新会失败,而 Spring 的@Value注入依赖的是配置变更事件,事件被异常中断后不会重放。
解决:先确认客户端日志里有没有Config changed字样,没有就查客户端是否真正连接到了 Config Service,可以用curl直接请求配置接口:curl http://192.168.1.100:8080/configs/sample-service/default/application,能返回 JSON 就说明服务端有配置。有日志但没生效,检查监听器方法内部是否抛了异常,把监听器逻辑精简为只打日志,确认事件流畅通后再加业务逻辑。另外注意,Apollo 发布是有版本概念的,修改后要点“发布”按钮,不是保存就算生效,这个操作很多人漏掉。
5.5 数据库连接串少了两个参数,页面乱码、写入超时
现象:Portal 页面能打开,但配置项里输入中文保存后变问号,或者偶发写入超时。
原因:数据库连接串没有指定characterEncoding,MySQL 客户端默认用了latin1做连接字符集;同时没指定serverTimezone时,在新版 MySQL 驱动下会报时区错误或者使用服务器本地时区导致时间不一致。
解决:在前面提到的三处数据源连接串中统一加上?characterEncoding=utf8&serverTimezone=Asia/Shanghai。注意utf8在 MySQL 里是utf8mb3的别名,如果你希望完全支持 emoji 字符,数据库和连接串都要用utf8mb4,连接串参数写成characterEncoding=utf8即可,因为 JDBC 驱动的utf8对应的是utf8mb4。改完连接串后重启三个服务,重新导入一次数据最稳妥。
6. 部署验收三步走:热更新、灰度回滚、缓存日志观测
服务端和客户端都跑通之后,不要急着写业务,先用三个小实验把 Apollo 的核心能力验证一遍。第一步是热更新验证。在 Portal 里新建一个配置项,比如test.refresh=true,在客户端代码里用@Value注入它,然后修改为false并发布。观察客户端日志,正常情况下 2 到 3 秒内会出现Config changed的打印,程序无需重启,值就变了。这一步过了,说明 Spring 集成和事件通知链路是通的。
第二步验证灰度发布与回滚。Apollo 的灰度是针对实例 IP 的,在 Portal 里把配置发布到灰度环境,指定某个客户端实例的 IP,然后检查只有那台机器收到了新值,其他机器保持旧值。回滚操作在发布历史里,找到上一次发布记录,点“回滚”即可。这个能力在生产环境非常关键,出了问题能在秒级恢复到上一个稳定版本,比重启快太多。
第三步看本地缓存与日志。找到客户端机器上~/data/{appId}/目录下的缓存文件,里面是配置的 JSON 快照,确认它和服务端一致。这一步的价值在于验证“客户端在服务端不可达时,如何用本地缓存兜底”。你可以把 Config Service 停掉,再重启客户端,观察它是否能用缓存启动,启动后配置是否正确。这个实验建议每个环境都做一遍。
我自己的习惯是部署完写一个一页纸的检查单,把端口、数据库、Eureka 地址、app.id 四项核对完再收工。这套流程走下来,后面接真实业务配置时基本不会再被底层问题绊住。希望帮到你。
本文还有配套的精品资源,点击获取