前言
一个具有规模的项目通常后端都会有几个和业务相关的服务模块,可能每个模块都是由不同的人负责开发和维护,模块之间多少会存在一些依赖。Java的项目中通常都会使用Maven进行管理,利用Maven的多态和坐标维护各个模块的生命周期。
当项目迭代到一定规模时,可能会出现有多个代码的分支同时进行开发,并且有多个测试环境由多个不同的测试人员进行测试,这个时候就会出现一个问题。如下图所示:
噩梦的开始
假如这里有三个人维护这6个分支,小陈、小杨和小谷。小陈这个时候需要编译发布下A模块的分支1到测试环境1中,他要求小杨先使用Maven Install一下模块B的分支1代码,这个时候小陈可以开始操作了。过了一会,小谷需要发布A模块的分支2到测试环境2中,所以也要求小陈Install一下模块B的分支2代码。So...小杨开始烦躁了。
过了几天,产品经理要求新开发一个模块功能,需要使用到A和B两个模块的业务接口,小杨这个时候突然情绪变得很不稳定,拳头紧握,面红耳赤,只有小陈和小谷理解小杨的心情。
当出现这个问题的时候,程序员需要花更多的时间在编译和发布上,而且还很容易出错,可能一不小心,会将线上版本所依赖的其他模块的内容,是其他分支的版本。
我们需要隔离
思考了很多种方式,由于是多模块依赖,并且在不同的环境中部署,最终应该需要按照部署环境隔离。首先,测试1和测试2两个环境使用的中间件和数据库都不一样,先从配置入手,Maven中提供了Profile的配置切换的机制,我们可以很容易的运用profile定义两套,例如stage1和stage2两个(之前的方式就是按照这种进行处理)。
接下来需要使用到Maven的一个版本管理的插件,需要在build中加入如下插件:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.3</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
这个插件能够根据配置或者环境变量修改pom中的版本号,功能有很多,详情可以参考官方文档http://www.mojohaus.org/versions-maven-plugin/
我们主要使用到versions:set这一部分。
pom的重构
虽然有这个versions,但是还是遇到了麻烦。我们的项目中有个顶级POM,此工程是核心库,可以称之为Core库,主要包含了一些有关安全、会话、工具和一些核心API。A和B两个模块都是直接继承在Core下面,并且Core、A和B都是放在不同的git仓库下独立维护。
versions:set可以在Core上面进行使用,但是A和B不行,因为A和B的根级pom是直接继承Core,也就是其根级的pom中包含了
<parent>
<groupId>core</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
这个的继承信息,versions:set不能改写模块的根级pom具有继承特性的项目。
最初模块按照这种继承的方式进行实现目的是为了让各个模块都按照Core所规范的坐标信息进行开发,这种方式确实给程序员带来了一些统一和规范。如果要解决这个环境隔离的发布问题,就必然要打破这种层级结构。相比环境隔离的问题的重要性,这种层次结构的规范可以打破。
重构方式很简单,将A和B的子模块去继承化,并拷贝Core根级下的dependencyManagement、properties、organization、distributionManagement和build等节点信息,放到A和B子模块的根级pom中。
子模块的Profile设置
versions:set需要配置-DnewVersion=xxxxx的环境变量使用,这里需要统一各个应用环境下的版本号,所以需要在各个模块的根级pom中设置这个环境变量,如下:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<newVersion>1.0-dev-SNAPSHOT</newVersion>
</properties>
</profile>
<profile>
<id>stage1</id>
<properties>
<newVersion>1.0-stage1-SNAPSHOT</newVersion>
</properties>
</profile>
<profile>
<id>stage2</id>
<properties>
<newVersion>1.0-stage2-SNAPSHOT</newVersion>
</properties>
</profile>
</profiles>
注意哦,这个是放置在根级!
开始编译和发布
经过上面几次配置,我们可以开始编译和发布了,需要分别对每个模块使用两个命令,如下是编译stage1环境:
mvn clean versions:set -P stage1
mvn clean install -P stage1
注意:这里一定要使用两次命令进行编译,之前曾尝试过用一个命令:mvn clean versions:set install -P stage1,后来发现,由于mvn首次加载时的pom文件还是之前未改过的版本号,虽然使用versions:set将pom文件的版本号都修改了一遍,但是mvn没有reload的机制,那么只会的install命令依然还是旧的版本号。
编译发布完之后,可以使用如下命令:
mvn versions:revert
将备份的pom文件进行回滚。
后记
本篇文章只提供了一套解决方案,具体情况还需要看项目团队的规模和内容而定,但是他确实可以解决不少问题。