先说结论:从 Spring Cloud 2020.0 起 bootstrap 上下文默认关闭,配置中心接得对不对,全看本地 application.yml 里那 4 个字段。少一个,服务要么直接起不来,要么起来了连的是另一套库。
我们这套是 Spring Boot 4.0.8 + Java 21 + Spring Cloud 2025.1.0 + Spring Cloud Alibaba 2025.1.0.0,服务端 Nacos 3.1.1。刚把 12 个业务服务的数据源从各自的 application.yml 搬进配置中心,一共涉及 14 个库,每个服务一个 Data ID。整个过程最花时间的不是"怎么连",而是搞清"连上了哪一份"。
一、为什么要搬
重组后的中台拆成了 12 个业务服务,数据库是每服务一库,个别服务还带一个只读从库,加起来 14 个库。迁移前每个模块的 application.yml 里都躺着一段完整的连接串:
- 切环境要改 12 个文件,漏一个就是连错库;
- 密码跟着代码进 git,换个环境还得翻提交历史;
- 测试库和生产的连接串长得几乎一样,肉眼 review 抓不住。
搬进配置中心之后,本地只留与环境无关的东西(服务名、端口、Redis、MyBatis-Plus 的全局策略),数据库连接一个服务一份,放在 Nacos 上。命名和服务对齐,一眼能对上:
| Data ID | 对应库 | 服务 |
|---|---|---|
demo-system.yaml |
demo_system |
系统管理 |
demo-oa.yaml |
demo_oa |
办公 |
demo-data.yaml |
demo_data |
数据服务 |
demo-pay-bill.yaml |
demo_pay_bill |
账单结算 |
其余 8 个同理,一个服务一个 Data ID、一份库。网关和认证不连业务库,不计入这 12 个。
二、4 个必填项
第 1 个:依赖
xml
<!-- 配置中心客户端;版本交给 Spring Cloud Alibaba BOM 管,别自己指定 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
顺手提醒一件老习惯要丢掉:不要再去找 bootstrap.yml 。Spring Cloud 2020.0 之后 bootstrap 上下文默认不加载,网上大量教程还停在"引 spring-cloud-starter-bootstrap + 写 bootstrap.yml"那套,照抄会得到一套根本不生效的配置------服务能起来,但拉的全是本地默认值。
第 2 个:spring.config.import(少这行,启动直接失败)
yaml
spring:
application:
name: demo-system
# 关键:显式声明要从配置中心导入哪个 Data ID
config:
import: nacos:demo-system.yaml
cloud:
nacos:
config:
# 拉配置用的地址只能写在本地:配置还没到手,没法从远端读到自己
server-addr: 127.0.0.1:8848
只加依赖、不写 import,启动会失败,报错里其实写得很直白,大意是:
text
No spring.config.import property has been defined
Action: Add a spring.config.import=nacos: property to your configuration.
这一行是"必填"而不是"可选",因为 Spring Cloud 现在把配置中心的接入统一收口到了 Boot 的 Config Data API:不声明导入,它就没有理由去连 Nacos。
第 3 个:Data ID 与 group 三个地方要一致
Data ID 在四个位置出现,任何一个对不上都拉不到配置:
spring.application.name(服务名);spring.config.import里的nacos:xxx.yaml;- Nacos 控制台上的 Data ID 字面量;
spring.cloud.nacos.config.group(不写就是DEFAULT_GROUP)。
这次我们统一成"服务名 + .yaml"一种写法,group 全部走默认。要注意扩展名参与解析 :.yaml 和 .yml 都按 YAML 解析,但漏写扩展名会退化成按 properties 读------一份 YAML 内容按 properties 解析,等于什么都没读到。而拉不到配置时的报错通常只告诉你"这个 location 不存在",不会提示"你是不是漏了扩展名"------所以先查字面量,别急着怀疑网络。
第 4 个:地址与认证(Nacos 3 默认开着认证)
server-addr 在第 2 项里已经出现了,这里要强调的是:它和第 2 项一样只能写在本地------配置还没到手,没法从远端读到自己。
我们用的 Nacos 3.1.1 镜像是带认证的(Bearer Token),客户端不带账号就拉不到配置:
yaml
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
# Nacos 3 默认开启认证,这里必须给能读该命名空间的账号
username: ${NACOS_USER}
password: ${NACOS_PASSWORD}
坑在于表现:认证失败在客户端往往只是"配置没拉到 → 启动失败",看不到 401/403 这类字眼。判断顺序是先看 Nacos 服务端日志有没有拒绝记录,再改客户端,否则很容易在客户端反复改 Data ID 白折腾。
三、反直觉的那一条:迁移期不要用 optional:
Spring Cloud 的报错提示里其实给了另一条出路:
text
If configuration is not required add spring.config.import=optional:nacos: instead.
optional:nacos:xxx.yaml 的意思是"这份配置拉不到也不当错误"。它在"该配置确实可有可无"的场景下是对的,但在迁移数据源这件事上,它等于把唯一的保险关掉了:
拉不到配置 → 不报错 → 走本地默认 → 服务起来了 → 连的是本地库或默认库 → 直到业务方看见数据不对才发现。
判断标准只有一句:这条配置缺了,服务还能不能正确工作? 不能,就不要加 optional:。它把"启动失败"这种一眼能看见的错,换成了"跑起来但是错的"。
四、迁移的四个真实代价
配置中心不是纯收益,这四条是我们实打实付出去的:
- 离线开发断了 。密码只在 Nacos,本地起服务必须先连上配置中心。我们的解法是本地留一份
application-local.yml存数据源,用 profile 激活时覆盖。注意这不是靠"谁的优先级高"来抢配置------两端 key 刻意不重叠(本地只放driver-class-name这类与环境无关的项),不用靠记忆判断谁生效。 - 单机 Nacos 内嵌 derby,配置跟着容器走。容器一重建(升级镜像、迁机、清理),12 个 Data ID 全丢。要么把数据目录挂出来,要么换成外部 MySQL 存储,别等出事才发现。
- 端口只监听
127.0.0.1,外面走统一入口 nginx 白名单 。这样最安全,但意味着服务必须和 Nacos 同机,跨机部署就得改server-addr并放开安全组。我们的 12 个服务和 Nacos 都在这台机上,所以这条暂时没成本;一旦拆机就是必改项。 - 12 个 Data ID 靠手工建,新环境迁移必漏一个。做法是把"Data ID ↔ 库 ↔ 服务"的对照表连同每个 Data ID 的模板(占位符,真实密码现场填)一起放进仓库,迁移时按表逐个打勾。密码永远不进 git,进 git 的只有模板和清单。
五、怎么确认"真的连上了远端"
只看到"服务起来了"不算验收,三条都要过:
- 启动日志 里能看到从 nacos 拉取配置的记录(
Located property source/ nacos 客户端相关日志),而不是一路本地加载。 - Nacos 控制台上该 Data ID 的监听查询里,能看到你这个服务实例的 IP。
- 反向验证:把远端 Data ID 里的库名故意改错一位,重启。服务必须启动失败或连接失败------如果照样起来,说明它根本没读远端,你读到的是本地默认值。
第 3 条最值得做,也最容易被跳过。它验证的不是"配置写得对不对",而是"配置有没有被用上",这两件事在配置中心里经常不是一回事。
六、一句话收口
配置中心的坑几乎都不在"怎么连",而在"连上了哪一份":本地把 4 个字段对齐(依赖、spring.config.import、Data ID 与 group、地址与认证),远端只留一份内容,中间不要用 optional: 掩盖缺失;再用"改错一位必须失败"的反向验证确认它真的生效了。
附一个同批遇到的版本坑:Spring Cloud 2025.x 里网关的配置前缀从
spring.cloud.gateway.*挪到了spring.cloud.gateway.server.webflux.*,旧前缀不再被读取,路由不生效但启动不报错------升级大版本时这类"静默失效"比编译错误危险得多。