外置application.yml读取异常Bug记录

外置application.yml读取异常Bug记录

排查问题

大年二十七,公司马上要放假了,结果接到客户反馈前不久更新的服务数据库链接异常,报错信息为ORA-01017。引起这个报错通常可能是下面的原因:

  1. Oracle用户过期

  2. Oracle用户被锁定

  3. 配置文件密码错误

  4. 配置文件的编码异常,无法正确被解析

对这些问题一一排查后,竟然都是正常的!瞬间我整个人不正常了,到底是什么问题呢??????

补充一下项目部署目录的文件结构:

|-app.jar
|-application.yml
|-application-prod.yml
|-start.sh

application.yml和application-prod.yml都和app.jar同级,并且application.yml中的active就是prod。

springboot配置文件读取优先级

我们知道在springboot项目中外置的yml文件优先级如下:

  1. file:./config/ ,即优先读取jar包外的config目录中的配置文件
  2. file:./ ,即第二顺序读取jar包外的config目录同级的配置文件
  3. /config ,即项目目录下和src同级的config目录
  4. classpath:/config/ ,即第三顺序读取jar包内classpath下/config文件夹内的配置文件
  5. classpath:/ ,即第四顺序读取jar包内classpath中的配置文件

如果在优先级更高的目录找到了可用的key,就会直接获取,未找到的key会在优先级第一级中继续获取。即最终生效的文件可能是好几个文件的合集。

注意:如果最高级别的active是prd,此级别却没有 prd环境的配置的话,即使更低优先级的目录有也不会采用,会直接报错。

解决办法

最终抱着死马当活马医的态度,我准备将程序的jar包解压,把外置配置文件替换到jar包内重新启动,解压后的目录结构如下:

# 解压jar包 到xxx目录
unzip xxx.jar -d xxx

# 也可以使用jar命令解压,但是jar命令无法指定解压到的目录
jar -xvf xxx.jar

解压后的文件目录如下:

|-BOOT-INF
|--classes
|--lib
|-META-INF
|-org

其中/BOOT-INF/classes内存放的是yml文件,将外置的yml文件复制到此目录下进行覆盖操作。

使用jar命令将文件压回

# 进入存放BOOT-INF、META-INF、org三个文件夹的目录
cd /xxx

# 在xxx目录执行此命令,压缩三个目录为jar包,取名为xxx.jar
jar cfM0 xxx.jar *

# 如果是windows机器打jar 提示没有此命令,可以在开始菜单搜索jar 确认jar的目录,例如在:C:/java/jdk/bin/jar.exe,可以在xxx目录用如下命令:
C:/java/jdk/bin/jar.exe -cfM0 xxx.jar *

将jar包打包放到服务器,删除所有外置文件重新启动

奇迹发生了,程序一切正常~~~~~~~终于可以安心过年了。。。。。。。。。。。

后续我又在测试环境检查了外置文件,默认读取的确实是外置的文件,甚至使用md5sum命令计算了两个jar包的MD5也是一样的。这个问题后续有空会好好研究一下,后续结果后更新~

更新,补充一种可能会导致不读取外部yml文件的可能

加入部署包位置在/opt/app,那么使用java -jar /opt/app/xxx.jar的形式运行jar包不会读取外部的yml文件,会默认读取jar包内部的。

只有先cd到jar包所在目录/opt/app,再执行java -jar xxx.jar才会生效。

优化建议

对于生产环境的程序,尽量增加一步操作:检验配置文件的准确性,因为外置配置文件和jar包内的key可以控制到一致,所以可以忽略key替换的问题,只需确认目前生效的是哪一个文件即可。

注意:下面案例中增加Version的办法仅适用于配置文件与内部完全一致的情况。

增加检测类

在yml文件中增加version,针对jar包内的文件和jar包外的version值进行区分。

application.yml内容

spring:
  profiles:
    active: dev

application-dev.yml内容

version: 1.0.0

检验代码,这里也可以替换为验证中间件链接是否OK,并打印状态:

package cn.vantee.runner;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

/**
 * @author :rayfoo@qq.com
 * @date :Created in 2022/1/30 11:03 下午
 * @description:启动类自动执行块
 * @modified By:
 * @version: 1.0.0
 */
@Component
//如果有多个runner需要指定一些顺序
@Order(1)
public class ApplicationRunner implements ApplicationRunner {

    @Value("${version}")
    private String version;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(version);
    }
}

启动类

package cn.vantee;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author :rayfoo@qq.com
 * @date :Created in 2022/1/30 10:59 下午
 * @description:启动类
 * @modified By:
 * @version: 1.0.0
 */
@SpringBootApplication
public class ApplicationStater {

    public static void main(String[] args) {
        SpringApplication.run(ApplicationStater.class,args);
    }

}

根据打印结果,就可以确认最终生效的是哪一个文件啦~

其他建议

针对数据库、中间件的连接健康状态检验,也可以在Runner中编写,验证的顺序可以使用@Order(X)控制

分割线

2022.3.1更新 破案啦~

关于外置文件读取异常的问题,原因是因为pom文件中的spring-boot-maven-plugin插件。https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/#goals-repackage-parameters-details-executable

使用某些插件打包会导致jar包被加密,无法使用解压工具打开替换其内部的文件(提示压缩包以损坏),也无法使用vim直接修改jar包内的内容。

建议包上生产前可以先用vim验证一下是否可以编辑。

posted @ 2022-01-30 22:48  张瑞丰  阅读(214)  评论(0)    收藏  举报