Java 动态打包配置文件
半野

1.背景

最近遇到一个需求:使用的中间件配置文件无法根据环境区分,但是不同环境的配置又不相同,于是打算更改jar打包方式,使用maven profile的方式进行打包。但是最后改来改去,发现Profile依然不能满足需求,最后只能通过在启动的时候,通过脚本动态更新jar包来做。

2.Maven Profile使用方式

2.1.添加配置文件夹

模块的根目录 下文件夹下添加 profiles 文件夹。文件夹的名字可以为任意,为了表明是maven profile使用的文件,这里取名为 profiles

profiles为每个环境创建一个相应的文件夹,最近文件夹的结构如下:

1
`module | |---- src |---- profiles | |---- staging | |---- application.conf |---- preview | |---- application.conf |---- prod-c1 | |---- application.conf |---- prod-c2 | |---- application.conf `

事实上,profiles文件夹放在resources下更好,但是为了和脚本动态更新jar包方式一起来说,将其和src放置为同一级目录了。

2.2.修改POM文件

在pom文件中的project节点下,添加profiles节点。profiles节点下面包含一系列的profile,其中id用于唯一标识这个profile,需要在编译的时候指定,profile下面的properties是在当前profile生效的时候,被激活的动态配置信息,这里使用cluster.env指示当前的环境。

build > resources > resource节点下,引用被激活的环境属性cluster.env,就可以实现动态的打包配置文件。

1
`<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> .... <build> <resources> <!-- ... --> + <resource> + <directory>../profiles/${cluster.env}</directory> + </resource> </resources> </build> + <profiles> + <profile> + <id>staging</id> + <properties> + <cluster.env>staging</cluster.env> + </properties> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + </profile> + <profile> + <id>preview</id> + <properties> + <cluster.env>preview3</cluster.env> + </properties> + </profile> + <profile> + <id>c1</id> + <properties> + <cluster.env>c1</cluster.env> + </properties> + </profile> + <profile> + <id>c2</id> + <properties> + <cluster.env>c2</cluster.env> + </properties> + </profile> + </profiles> </project>`

2.3.修改打包命令

通过maven profile的形式打包配置文,需要在编译代码时,指定使用的profile的id,通过-P[profileName]指令来实现,例如:

1
`mvn -U -X -e clean package -am -Pstaging `

这里就是使用了staging下面的配置文件,maven会把staging这个profile对应的文件../profiles/${cluster.env}下的所有文件打包到jar包中。

2.4.修改Dockerfile

需要在生成镜像的时候,将profiles整个文件夹打包到镜像中。我们Dockerfile放置的位置在工程的根目录,即和.idea同级。

1
`COPY application/profiles /opt/application/[module name]/profiles `

2.5.shell脚本

我们可以通过maven profile的方式,实现配置文件的动态打包。但是线上环境存在多个集群,不同的集群使用的配置文件也不同,例如prod-c1prod-c2。我们不可能为每个集群都打包一个镜像,只能通过在启动容器的时候,通过环境变量动态指定集群,来使用不同的配置文件,这样maven profile打包的形式,对我们来说就不可用了,所以只能通过shell脚本,在启动前检查集群信息,动态更新jar包来实现。

脚本如下:

1
`# 项目根目录,appassembler 文件夹的父路径 # CUR_MODULE 来自环境变量,指定当前应用的在工程下的哪个模块 # CUR_CLUSTER 来自环境变量,指定当前的集群 project_home="/opt/application/${CUR_MODULE}" jar_dir="${project_home}/${CUR_MODULE}/appassembler/repo" jar_name="${CUR_MODULE}-1.0.0-SNAPSHOT" jar_path="${jar_dir}/${jar_name}.jar" function conf_dynamic() { # 省略:判断CUR_MODULE是否存在、CUR_CLUSTER配置文件是否存在 # 当前集群的配置文件路径,需要在Dockerfile中将profiles整个文件夹打包到镜像里相应的位置 conf_dir="${project_home}/profiles/${CUR_CLUSTER}" if [ -d "${conf_dir}" -a -e "${jar_path}" ]; then cd ${jar_dir} # 复制配置文件到jar包所在的文件夹 cp ${conf_dir}/* ${jar_dir} # 更新配置文件 jar -uvf ${jar_path} *.properties *.conf *.yml else echo "dir find err, conf_dir: ${conf_dir} -> jar_path: ${jar_path}" exit 1 fi } # 执行动态配置脚本 conf_dynamic # ... `

jar -uvf 命令可以用于更新jar包,使用可以参考:https://segmentfault.com/a/1190000012019605

3.注意项

profile中的配置文件不能相互引用,例如:profiles/preview/application-preview.conf不能引用 profiles/c1/application-c1.conf,因为profile中的配置文件不会同时出现在classpath下。但是可以引用resources/application-prod.conf,只要名字不和profile中的配置文件重名即可。

由 Hexo 驱动 & 主题 Keep
总字数 105.7k 访客数 访问量