Administrator
发布于 2021-06-05 / 4920 阅读
80

Spring Boot 2.4 配置文件变更与迁移注意

升级到 2.4,服务起不来了

6 月初,把一个服务从 Spring Boot 2.3.7 升到 2.4.6。改完 pom 里的版本号,启动,直接炸:

***************************
APPLICATION FAILED TO START
***************************

Description:

Property 'spring.profiles' imported from location
'class path resource [application-prod.yml]' is invalid in a profile specific resource
[origin: class path resource [application-prod.yml] - 3:13]

Action:

See https://docs.spring.io/spring-boot/docs/2.4.x/reference/html/spring-boot-features.html

这次升级前我以为只是改个版本号的事,结果 2.4 的配置文件处理逻辑整个重写了。翻了官方文档和 release notes 才搞明白。

2.4 改了什么

Spring Boot 2.4 引入了一套新的配置文件加载机制,官方叫 Config Data API。它替换了原来的 ConfigFileApplicationListener。对我们影响最大的是三点。

变化一:spring.profiles 在多文档 YAML 里被废弃

我们原来的 application.yml 是这么写的(多文档格式,用 --- 分隔):

spring:
  application:
    name: order-service
  profiles:
    active: dev

---
spring:
  profiles: dev          # 2.4 里这么写会报错

server:
  port: 8080

---
spring:
  profiles: prod         # 弃用

server:
  port: 8081

2.4 要求改成:

spring:
  application:
    name: order-service
  profiles:
    active: dev

---
spring:
  config:
    activate:
      on-profile: dev

server:
  port: 8080

---
spring:
  config:
    activate:
      on-profile: prod

server:
  port: 8081

spring.profilesspring.config.activate.on-profile。名字变长了,但语义更清楚:这是一个激活条件,不是"声明这个文档属于哪个 profile"。

变化二:profile-specific 文件里不能再激活别的 profile

这条限制是变化一带来的连锁反应。application-prod.yml 里写 spring.profiles.activespring.profiles.include,2.4 会直接拒绝。

我们正好踩了:原来有个 application-prod.yml 里写了:

spring:
  profiles:
    include:
      - redis-prod
      - mq-prod

目的是激活 prod 的同时,把 application-redis-prod.ymlapplication-mq-prod.yml 也带上。2.4 里这写法在 profile-specific 文件中不允许。

报错:

Property 'spring.profiles.include' imported from location
'class path resource [application-prod.yml]' is invalid in a profile specific resource

三种改法:

# 改法一:把 include 挪到主文件(非 profile-specific)
# application.yml
spring:
  profiles:
    active: prod
    include:
      - redis-prod
      - mq-prod

# 改法二:用 group(2.4 新增,推荐)
# application.yml
spring:
  profiles:
    active: prod
    group:
      prod:
        - redis-prod
        - mq-prod
      test:
        - redis-test
        - mq-test

# 改法三:用多文档 + config.activate.on-profile,在文档里用 include
# application.yml
spring:
  profiles:
    active: prod
---
spring:
  config:
    activate:
      on-profile: prod
  profiles:
    include:            # 在 activate 文档里 include 是允许的
      - redis-prod

我们最后用了改法二 spring.profiles.group。这是 2.4 新增的特性,把"激活 prod 等于激活哪一组"这件事收敛到一处,比散在各个文件里清楚太多。

变化三:新增 spring.config.import

这是个纯粹的新能力,可以引入额外的配置文件,而且优先级比当前文件高(后导入的覆盖先导入的)。

# application.yml
spring:
  config:
    import:
      - classpath:common/redis.yml
      - optional:file:/etc/order/override.yml     # optional: 前缀表示文件不存在也不报错
      - configtree:/etc/order/secrets/            # 配置树,K8s 挂载 secret 时很好用

configtree: 这个前缀我们后来用上了。K8s 里把 secret 挂载成目录,每个文件是一个配置项,文件名是 key,内容是 value:

/etc/order/secrets/
├── db-password        # 文件内容就是密码
└── redis-password

直接就能用 ${db-password} 引用,不用再写环境变量映射。

升级前先跑一遍 migrator

这是官方提供的工具,能自动扫描并报告需要改的配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-properties-migrator</artifactId>
    <scope>runtime</scope>
</dependency>

启动时它会打印所有需要迁移的属性:

The use of configuration keys that have been renamed was found in the environment:

Property source 'applicationConfig: [classpath:/application-prod.yml]':
    Key: spring.profiles
        Line: 3
        Replacement: spring.config.activate.on-profile

Property source 'applicationConfig: [classpath:/application-mq.yml]':
    Key: spring.profiles.include
        Line: 8
        Reason: Profile specific files are not allowed to activate other profiles.

它甚至会在运行时帮你把旧属性自动映射到新属性上,所以带这个依赖可以先让服务跑起来,然后再逐个改。等全部改完,把这个依赖删掉。

我们就是这么做的:加上 migrator → 服务能正常启动 → 按报告逐个改配置 → 全改完 → 删掉依赖 → 重启验证。

兜底开关:legacy processing

如果一时改不完,2.4 保留了旧的处理逻辑,加一行配置就能回退:

spring:
  config:
    use-legacy-processing: true

我们中间态用过一周(有 3 个服务配置多,改不完),但官方明确说了这个开关会在未来版本移除,不能长期依赖。

我们实际改了什么

文件改动数量
application.ymlspring.profilesspring.config.activate.on-profile4 处
application-*.yml移除 profile-specific 文件里的 include7 处
application.yml改用 spring.profiles.group3 组
application.yml新增 spring.config.import1 处

七个服务,累计改了不到 40 行配置,加 migrator 验证,一共花了两天。

另外两个 2.4 的变化

profile 的激活顺序影响最终值

2.4 里,后激活的 profile 覆盖先激活的。这个规则以前就有,但新版多文档格式让顺序更容易搞错。

spring:
  profiles:
    group:
      prod:
        - redis-prod
        - mq-prod
        - override       # 放最后,它会覆盖前面的同名配置

我们专门加了一个 override profile 放在组末尾,用于线上临时调整参数,改它不会影响其他 profile 文件。

测试里的 @ActiveProfiles

单测里写 @ActiveProfiles("test") 的部分没受影响,但如果你在测试资源目录里也用了多文档 YAML,同样要改。我们有 12 个测试类的 application-test.yml 需要同步修改。

小结

  • 2.4 重写了配置文件加载机制(Config Data API),spring.profiles 在多文档 YAML 中要改成 spring.config.activate.on-profile
  • profile-specific 文件(application-prod.yml)里不能再写 spring.profiles.include,这是最常见的启动失败原因。
  • spring.profiles.group 替代散落各处的 include,我们把"激活 prod 等于激活哪些"收敛到了一处。
  • 升级前加 spring-boot-properties-migrator 依赖,它会打印所有需要改的地方并在运行时临时兼容,改完再删掉。
  • 来不及改就用 spring.config.use-legacy-processing=true 回退,但别长期依赖。
  • spring.config.importconfigtree: 是 2.4 的好东西,K8s 挂 secret 时特别顺手。

这次升级真正的教训不是 2.4 改了什么,而是我们之前把 profile 的 include 关系散落在七八个文件里,所以改起来才这么麻烦。借这次升级把它们收敛成 spring.profiles.group 一处,算是因祸得福。

参考