Maven 新一代构建工具 mvnd

摘要

mvnd 简介

  • mvnd 是 Maven 新一代构建工具,基于 Netty 构建,使用 Java 语言编写。

  • mvnd 借鉴 Gradle 和 Takari 中的技术从而提供更快的Maven构建速度。

架构概述

  • mvnd内嵌Maven(因此不需要单独安装Maven)。1.x版本内嵌maven3.x版本,2.x版本内嵌maven4.x版本。

  • 实际构建发生在一个长驻后台进程中,又名守护程序。

  • 一个守护程序实例可以服务来自mvnd客户端的多个连续请求。

  • mvnd客户端是使用GraalVM构建的原生可执行文件。与启动传统的JVM相比,它启动速度更快,占用的内存更少。

  • 如果没有闲置守护程序来服务构建请求,则可以并行生成多个守护程序。

  • 这种架构带来了以下优点:

    • 不需要每次构建重新启动 JVM,大大节省时间。
    • 持有Maven插件类的classloaders被缓存在多个构建中。因此,插件jar只被读取和解析一次。Maven插件的SNAPSHOT版本没有缓存。
    • JVM内Just-In-Time(JIT)编译器生成的原生代码也被保留。与传统的Maven相比,JIT编译花费的时间更少。在重复构建期间,JIT优化的代码立即可用。这不仅适用于来自Maven插件和Maven Core的代码,也适用于来自JDK本身的所有代码。
  • 默认情况下,mvnd 使用多个 CPU 内核并行构建模块,使用的核心数由以下公式给出: availableProcessors - 1

mvnd 安装

1
2
3
4
5
6
7
8
9
# 下载 mvnd 对应的安装包
curl -L https://github.com/apache/maven-mvnd/releases/download/1.0.3/maven-mvnd-1.0.3-darwin-amd64.tar.gz | tar xz
ln -s mvnd-1.0.3-darwin-amd64 mvnd
# 添加 mvnd 到环境变量
echo 'MVND_HOME=$HOME/develop_soft/mvnd' >> ~/.zshrc
echo 'export PATH=$MVND_HOME/bin:$PATH' >> ~/.zshrc
source ~/.zshrc
# 检查 mvnd 是否安装成功
mvnd --version
  • mvnd 内嵌 maven,其本质上还是依赖于maven,所以需要创建maven的配置文件 ~/.m2/settings.xml ,由于本机之前安装过 Maven,所以这一步就省略了。

  • [推荐]将 mvnd 的配置文件拷贝到 ~/.m2 目录下

1
2
# 与 maven了类似,`~/.m2/mvnd.properties` 优先级高于 `$MVND_HOME/conf/mvnd.properties`
cp $MVND_HOME/conf/mvnd.properties $HOME/.m2/mvnd.properties
  • mvnd 配置项说明,一般不需要修改,可能会修改的我用 [*] 做了标注

配置项 默认值 中文解释
mvnd.noBuffering [*] false 是否禁用输出缓冲,从而像普通 Maven 一样实时显示日志。命令行传递 -B--batch-mode 也会启用此行为
mvnd.rollingWindowSize 0 构建并行模块时,每个模块显示的日志行数
mvnd.logPurgePeriod 7d 自动清理日志的周期(如:7天),日志保存路径: $HOME/.m2/mvnd/registry/1.0.3/
mvnd.noDaemon false 禁止使用 daemon(守护进程)。一般用于调试,仅在非 native 模式下有效
mvnd.debug false 使用调试模式启动 daemon,JVM 参数为:-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000
mvnd.idleTimeout 3 hours 守护进程在空闲多久后自动关闭
mvnd.keepAlive 100 ms 如果构建过程没有输出,daemon 向客户端发送保活消息的时间间隔
mvnd.maxLostKeepAlive 30 允许丢失的最大保活消息数量,超过则客户端认为 daemon 已失败
mvnd.minThreads 1 构建时要使用的最少线程数量。如果显式指定了 -T--threadsmvnd.threads,该值将被忽略
mvnd.threads [*] 未设置 传递给 daemon 的线程数,与 Maven 的 -T / --threads 语法一致
mvnd.builder smart 指定使用的 Maven 构建器名称(等同于 -b--builder
mvnd.minHeapSize 128M 守护进程 JVM 的最小堆内存
mvnd.maxHeapSize [*] 2G 守护进程 JVM 的最大堆内存
mvnd.threadStackSize 1M 守护进程线程栈大小
mvnd.jvmArgs 未设置 传递给 daemon 的额外 JVM 参数
mvnd.enableAssertions false 是否为 daemon 启用 JVM 断言(-ea)
mvnd.expirationCheckDelay 10 seconds 守护进程检测自身是否需要过期的时间间隔
mvnd.duplicateDaemonGracePeriod 10 seconds 多个 daemon 存在时,多余 daemon 的宽限退出时间
mvnd.home 自动设置 mvnd 安装目录,客户端通常会根据 mvnd 可执行文件位置设置
java.home 使用环境变量 启动 daemon 的 Java 目录(等同 JAVA_HOME)
maven.settings [*] ~/.m2/settings.xml maven 的 settings.xml 路径

mvnd 使用

  • 命令行使用方式与 mvn 一样,比如:

1
2
mvnd clean install
mvnd clean package -Dmaven.test.skip=true
  • mvnd 的 daemon 缓存了一切,所以运行 mvnd clean install 后,之后再次运行 mvnd clean install 时,mvnd 会直接从缓存中读取构建结果,从而节省了构建时间。

  • mvnd 还有些特殊用法,比如:

1
2
3
4
# 查看当前所有的守护进程列表,守护进程空闲超过 mvnd.idleTimeout=3 hours 会自动关闭
mvnd --status
# 手动停止所有运行的守护进程
mvnd --stop
  • 在IDEA中使用mvnd,在Maven设置中将mvnd的安装目录添加到 Maven Home Path

mvnd 与 mvnw 的区别

  • 我们现在大部分创建的项目都是Springboot项目,通过IDEA创建Springboot项目时会自动创建如下文件

1
2
3
.mvn: 文件夹,其内部存放了 `wrapper/maven-wrapper.properties` 文件,该文件用于声明  mvn 的 url
mvnw: linux 脚本文件
mvnw.cmd: windows 脚本文件
  • 实际上 mvnw 是一个 Maven 启动脚本,用于自动下载指定版本的Maven,并运行 Maven 构建。

  • 第一次使用mvnw时会通过wrapper/maven-wrapper.properties 文件中的声明自动下载maven,并保存在 ~/.m2/wrapper/dists/ 目录下

  • mvnw使用方式如下:

1
./mvnw clean install
  • 运行速度: mvnd >> mvnw ≈ mvn

对比项 mvnd mvnw(Maven Wrapper)
是什么 一个「常驻的 Maven 守护进程」 一个「Maven 启动脚本」
是否常驻 ✅ 是(daemon) ❌ 否(一次性)
是否下载 Maven ❌ 不下载,自己就是程序 ✅ 会自动下载指定版本
主要目的 加速构建 保证版本一致
是否推荐在 CI 一般不建议 ✅ 非常推荐
使用方式 mvnd clean install ./mvnw clean install
是否与项目绑定 ❌ 全局使用 ✅ 和项目绑定