写点什么

Maven 实战(四)——基于 Maven 的持续集成实践

  • 2011-02-04
  • 本文字数:4374 字

    阅读完需:约 14 分钟

Martin 的《持续集成》

相信很多读者和我一样,最早接触到持续集成的概念是来自 Martin 的著名文章《持续集成》,该文最早发布于 2000 年 9 月,之后在 2006 年进行了一次修订,它清晰地解释了持续集成的概念,并总结了 10 条实践,它们分别为:

  • 只维护一个源码仓库
  • 自动化构建
  • 让构建自行测试
  • 每人每天向主干提交代码
  • 每次提交都应在持续集成机器上构建主干
  • 保持快速的构建
  • 在模拟生产环境中测试
  • 让每个人都能轻易获得最新的可执行文件
  • 每个人都能看到进度
  • 自动化部署

原始文章距今已 10 年有余,这在软件行业中算是很长的时间了,但我们都能看到 Martin 总结的这些实践依旧闪耀着光芒,依旧有很多团队在努力实践它们并得到了丰厚的回报,当然也有很多团队因为各种原因拒绝实践持续集成从而无法体会到个中好处。

从这 10 条实践中我们能找到很多流行开源工具的影子,例如版本控制工具 cvs、svn、git,自动化构建工具 Maven、Ant,自动化测试框架 JUnit、TestNG,以及持续集成服务器 CruiseControl 和 Hudson 等等。其实不论你是否实践持续集成,单独使用这其中的很多工具都能发挥极大的价值,持续集成的一大意义在于它引入了一个有效的流程,能让这些工具有机融合,并相互促进。 关于持续集成还有一本获得 Jolt 大奖的图书,名为《持续集成——软件质量改进和风险降低之道》。但无论是Martin 的文章,还是这本图书,都没有阐述使用Maven 作为自动化构建工具实施持续集成的细节。本文旨在介绍一些基于Maven 实施持续集成的实践,希望这些经验能从具体处帮助到读者。

架设私有Maven 仓库

Martin 的文章并没有涉及到依赖管理的内容,但在 Java 的世界中,依赖管理是开发人员不得不面对的问题。无论是外部的开源类库依赖,还是项目内部的模块间依赖,都需要有效地管理。可以说依赖管理是持续集成核心的内容之一。Maven 通过其依赖管理机制和随处可用的中央仓库有效地解决了这个问题,用户只需要在POM 中声明项目所需要的依赖,Maven 就能在构建的时候自动从仓库解析依赖。

不过仅仅这样是不够的,我们知道,持续集成的最大好处在于降低风险,简单地来说就是尽早暴露问题,能让开发人员及早发现并修复,从而降低修复成本。可是,如果每个人都从中央仓库重复下载依赖,这是非常耗时的,集成的反馈周期肯定会延长。我已经无数次听到有人抱怨“Maven 在下载整个Internet!”。构建要快!持续集成反馈要快!Maven 你不能拖慢这个流程。

幸运的是开源世界有很好的解决方案,只要使用Maven 仓库管理器软件如 Nexus 建立一个私有的 Maven 仓库,问题就能迎刃而解。原理很简单,这个位于局域网内的 Maven 仓库能够代理所有外部仓库,从而避免所有人从 Internet 重复下载依赖文件。这样 Maven 解析依赖的时候仅限于局域网,构建速度就大大地加快了。例如大家都需要使用junit-4.8.2.jar,当第一个人向私有仓库请求的时候,私有仓库从中央库下载并缓存下来,假设耗时 10s,之后其他人需要junit-4.8.2.jar的时候,私有仓库直接使用缓存的文件,这个耗时可能就是 1s。如果有 100 个开发人员使用该文件,那节省的时间就是 100 * 10 - ( 10 + 99 * 1 ) = 891s ,实际情况中依赖的数量可能会是成百上千,那节省的时间就变得非常的可观。

也许有人会说,我也完全可以将项目依赖加入到版本控制中,这一点甚至在《卓有成效的程序员》中都被明确提及,在该书第5 章的"DRY 版本控制"一节中,Neal Ford 有这么一段话:“所有用来构建项目的东西都应该被放入版本控制,包括二进制文件(类库,框架,JAR 文件,构建脚本等等)”。作者进一步解释了其目的,这么做能够保证项目不受外部因素影响(如依赖版本变化,甚至丢失),保证构建的稳定,作者也同时提及了一般版本控制工具处理二进制文件的性能问题。抛开这条结论性的实践,仔细考虑其目的,我们就能发现,私有Maven 仓库同样能保证构建的稳定,而且能避免版本控制工具处理二进制文件而造成的潜在性能问题。所以,我斗胆说一句,Neal Ford 所提的这条实践OUT 了!

私有Maven 的仓库的意义还不仅限于此,结合自动化部署和Maven 的SNAPSHOT 机制,它能大大促进项目集成的效率。

在模块化的开发环境中,大家各司其职,专注于自己所负责的模块,持续集成的规则是,在往版本控制提交代码前,需要先保证本地构建没有问题,那一般的做法就是更新所有模块的代码并构建。可是,真的需要构建那些其实你并不怎么关心的模块么?且不谈一旦构建他人代码时出错,你往往会不知所措,这种做法同时也增加了本地构建的时间。

Maven 有 SNAPSHOT 版本的概念,其目的就是让你能够构建一个临时的版本,供团队他人使用,这样他们就不必在代码的层次关心自己的依赖。于是私有 Maven 仓库就充当了一个中介的作用,而持续集成服务器就多了一个职责,每次它成功构建一个模块,都应该将该模块的 SNAPSHOT 版本发布到 Maven 仓库中。现在,大家就不用去构建别人的代码了,Maven 能自动帮你从私有仓库解析下载依赖的最新 SNAPSHOT(使用mvn命令的 **-U参数强制更新)。注意,除了持续集成服务器外,任何其他人都不应该发布 SNAPSHOT 版本到 Maven 仓库,因为只有持续集成服务器的环境是可信任的,你能在本地成功执行mvn clean install** 并不代表持续集成服务器上该命令能成功,由于每个人的本地环境各有差异,因此集成的成功与否应当以持续集成服务器为准,而只有集成成功后,SNAPSHOT 才可以被部署到私有仓库供他人使用。

鉴于上述的原因分析,我认为在基于 Maven 的持续集成环境中,再怎么强调私有 Maven 仓库的重要性都是不为过的。

正确的集成命令

在持续集成服务器上使用怎样的 mvn 命令集成项目,这个问题乍一看答案很显然,不就是 mvn clean install 么?事实上比较好的集成命令会稍微复杂些,下面是一些总结:

  • 不要忘了 clean: clean 能够保证上一次构建的输出不会影响到本次构建。
  • 使用 deploy 而不是 install: 构建的 SNAPSHOT 输出应当被自动部署到私有 Maven 仓库供他人使用,这一点在前面已经详细论述。
  • 使用 -U 参数: 该参数能强制让 Maven 检查所有 SNAPSHOT 依赖更新,确保集成基于最新的状态,如果没有该参数,Maven 默认以天为单位检查更新,而持续集成的频率应该比这高很多。
  • 使用 -e 参数:如果构建出现异常,该参数能让 Maven 打印完整的 stack trace,以方便分析错误原因。
  • ** 使用 -Dmaven.repo.local 参数:如果持续集成服务器有很多任务,每个任务都会使用本地仓库,下载依赖至本地仓库,为了避免这种多线程使用本地仓库可能会引起的冲突,可以使用-Dmaven.repo.local=/home/juven/ci/foo-repo/** 这样的参数为每个任务分配本地仓库。
  • 使用 -B 参数:该参数表示让 Maven 使用批处理模式构建项目,能够避免一些需要人工参与交互而造成的挂起状态。

综上,持续集成服务器上的集成命令应该为 mvn clean deploy -B -e -U -Dmaven.repo.local=xxx 。此外,定期清理持续集成服务器的本地 Maven 仓库也是个很好的习惯,这样可以避免浪费磁盘资源,几乎所有的持续集成服务器软件都支持本地的脚本任务,你可以写一行简单的 shell 或 bat 脚本,然后配置以天为单位自动清理仓库。需要注意的是,这么做的前提是你有私有 Maven 仓库,否则每次都从 Internet 下载所有依赖会是一场噩梦。

用好 Profile

如果不需要考虑各种不同的环境, 而且你的自动测试(包括集成测试)跑得飞快,那你就不用为项目建立多个集成任务。但实际的情况是,集成的时候可能要考虑各种环境,例如开发环境、测试环境、产品环境。而当项目越来越大,测试越来越多,控制构建时间在一个可接受的范围内(例如 10 分钟)变得越来越不现实。《持续集成——软件质量改进和风险降低之道》中介绍了一种名为分阶段构建(staged build)的解决方案,例如你可以将构建分为两个部分,第一部分包括了编译和单元测试等能够快速结束的任务,第二个部分包括集成测试等耗时较长的任务,只有第一部分成功完成后,才触发第二部分集成。这么做的意义在于让持续集成的反馈尽可能的快。

Maven 的 Profile 机制能够很好的支持分阶段构建。例如,借助 Maven Surefire Plugin ,你可以统一单元测试命名为**UT,统一集成测试命名为**IT,然后配置 Maven Surefire Plugin 默认只运行单元测试,然后再编写一个名为 integrationTest 的 Profile,在其中配置 Maven Surefire Plugin 运行集成测试。然后再以此为基础分阶段构建项目,第一个构建为 mvn clean install -B -e -U ,第二个构建任务为 mvn clean deploy -B -e -U -PintegrationTest 。前一个构建成功后再触发第二个构建,然后才部署至 Maven 仓库。值得一提的是,Maven Surefire Plugin 能够很好支持 JUnit 3、JUnit 4 和 TestNG,你可以按照最适合自己的方式来划分单元测试和集成测试。

另一个常见的分阶段构建案例是生成 Maven 站点,使用 mvn clean site 生成站点往往比较耗时且耗资源,这样的任务对应的持续集成中的 _ 持续审查 _ 阶段,该阶段往往不需要很高的集成频率。你会希望每 10 分钟就检查源代码变更并编译测试,但很少有人会希望每 10 分钟让系统生成一次测试覆盖率报告、CheckStyle 报告等内容,因此合理的做法是使用一个较低的频率,例如每天,这样可以避免无谓的资源消耗,更重要的是,这样不会拖慢本该很快的编译和单元测试等反馈内容。

还有一些情况是系统需要基于不同环境进行集成,这时候就需要用到 Maven 的属性机制、资源过滤、以及前面提到的Profile。篇幅原因,这里不再展开。

小结

持续集成是敏捷最重要的实践之一,但如何在基于Maven 的环境下实践持续集成却鲜有文章详述,本文介绍了一些该主题的最佳实践,包括架设私有仓库、使用正确的集成命令、利用Profile 等技术处理分阶段构建等等。本文旨在让广大Maven 用户认识到这些实践的存在及重要性,并没有详细解释一些诸如Nexus 安装配置、Maven Surefire Plugin 配置、或者说Profile 配置使用方面的细节,如果你希望看到更细节的介绍,可以参考我的《Maven 实战》一书。除了上面的内容之外,该书还详细解释了如何使用Hudson(也许该改称Jenkins 了)这一最流行的开源持续集成服务器。当然,如果你有关于Maven 和持续集成方面的经验,也请不吝分享。

关于作者

许晓斌(Juven Xu),国内社区公认的Maven 技术专家、Maven 中文用户组创始人、Maven 技术的先驱和积极推动者。对Maven 有深刻的认识,实战经验丰富,不仅撰写了大量关于Maven 的技术文章,而且还翻译了开源书籍《Maven 权威指南》,对Maven 技术在国内的普及和发展做出了很大的贡献。就职于Maven 之父的公司,负责维护Maven 中央仓库,是Maven 仓库管理器Nexus(著名开源软件)的核心开发者之一,曾多次受邀到淘宝等大型企业开展Maven 方面的培训。此外,他还是开源技术的积极倡导者和推动者,擅长Java 开发和敏捷开发实践。他的个人网站是: http://www.juvenxu.com

2011-02-04 00:0022163

评论 2 条评论

发布
用户头像
maven -e 参数,打印验证maven 堆栈信息,应该用处挺大,下次 maven报错时,我也试试,谢谢分享
2022-08-16 07:55 · 北京
回复
用户头像
maven surefire plugin配置profile用于不同阶段的持续集成
2021-12-15 21:46
回复
没有更多了
发现更多内容

数字孪生可视化技术打造未来智慧码头系统

2D3D前端可视化开发

物联网 智慧港口 数字孪生 智慧码头 5G智慧港口

如何在Android安卓环境运行小程序游戏

Onegun

安卓 andiod 小游戏

应用并管控“两库”是信创软件安全的核心能力

云起无垠

Fuzzing

JAVA中的注解可以继承吗?

JAVA旭阳

Java

如何设计一个高性能的图 Schema

NebulaGraph

图数据库 图建模

服开与编排,老兵新传

鲸品堂

电信运营商 12 月 PK 榜

我们是如何追逐元宇宙、XR等“概念股”浪潮的?

阿里巴巴终端技术

3D渲染 3D vr

新思科技发布第13版软件安全构建成熟度模型报告

InfoQ_434670063458

安全评估 新思科技 BSIMM

什么是BPM系统?BPM流程管理系统介绍

优秀

BPM 业务流程管理

手动测试依然很重要

FunTester

易观千帆 | 10月手机银行APP用户体验GX评测

易观分析

手机银行 GX评测

自助取数、即席分析...瓴羊Quick BI助力企业数字化转型

对不起该用户已成仙‖

企业数字化转型关键路径:构建数据驱动的管控体系

元年技术洞察

数字化转型 数据驱动 方舟平台

掌握分布式环境缓存更新策略,提高缓存与数据库双写一致性!

C++后台开发

数据库 redis 分布式 中间件 后端开发

小游戏开发者变现攻略

Onegun

小程序 超级app 小游戏

持续应用安全(CAS)研讨之:Fuzzing

云起无垠

Guitar Pro2023吉他软件最新版本安装包下载

茶色酒

Guitar Pro Guitar Pro8

软件测试丨基于Junit4,利用xUnit框架让你的测试用例可维护性大幅提升

测试人

软件测试 单元测试 自动化测试 测试框架 测试开发

YMatrix 创始人姚延栋,获“最具发展潜力与创新影响力的创业者”称号

YMatrix 超融合数据库

创业 超融合数据库 YMatrix

为云原生插上翅膀,天翼云弹性存储CStor-CSI助力容器腾飞

天翼云开发者社区

容器 云原生 云存储

多样化数据看板,瓴羊Quick BI满足企业经营管理需求

对不起该用户已成仙‖

构建数字时代下的软件供应链安全体系

云起无垠

软件 软件供应链安全

带你手把手实操一个RPC框架

得物技术

架构 中间件 java client prc 12 月 PK 榜

做7秒动画赢13W大奖?总奖池超80W、国内最火爆的3D渲染动画创作大赛开始报名!

Renderbus瑞云渲染农场

3D渲染动画大赛 3D动画制作 瑞云渲染CG竞赛

开发小游戏的流程及难点汇总

Onegun

小程序 小程序容器 小游戏 小游戏开发

如何绘制甘特图?这里有一份最全的教学指南(建议收藏使用)!

PMO实践

甘特图 PMO 项目经理

matic链佛萨奇系统开发源代码快速部署上线

开发微hkkf5566

DTCC2022预告 | 玖章算术叶正盛:程序员必须掌握的数据库原理

NineData

数据库 数据迁移 数据管理 DTCC2022 NineData

盘点新能源汽车常用的8种传感器

元器件秋姐

传感器 新能源汽车 智能传感器 新能源 IGBT

Python 缩进语法的起源:上世纪 60-70 年代的大胆创意!

Python猫

Python

去哪儿是如何做到大规模故障演练的?

TakinTalks稳定性社区

自动化 混沌工程 故障演练

Maven实战(四)——基于Maven的持续集成实践_Java_许晓斌_InfoQ精选文章