本文成文于 2019 年。最近 Apache StreamPark (Incubating) 项目要做第一个 Apache 版本的发布,遇到了类似的发布多 Scala 支持版本时如何正确生成对应 POM 文件,又尽可能复用流水线的问题。由于过往发布记录都被删除,故重新发布。
近日在阅读 FLINK 代码时发现 FLINK 有一个 force-shading
模块,关于这个模块的作用注释在其使用点 maven-shade-plugin
的配置中是这样写的
现在这个模块已经移动到 flink-shaded 仓库下,详见 pom.xml 文件。
1 2 3 4 5 6 7 8 9 10 11 <artifactSet > <includes > <include > org.apache.flink:force-shading</include > </includes > </artifactSet >
从注释中我们可以看到 force-shading
的作用是强制触发 maven-shade-plugin
的执行,并且提到了这样会生成所谓的 effective pom 文件。这究竟是怎么一回事呢?我们先从一个实例中理解这个问题。
先创建一个任意 MAVEN 工程,将它的 pom.xml 文件中 dependencies
替换为以下内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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" > <modelVersion > 4.0.0</modelVersion > <groupId > moe.tison</groupId > <artifactId > eden_${scala.binary.version}</artifactId > <version > 0.1-SNAPSHOT</version > <properties > <scala.binary.version > 2.12</scala.binary.version > </properties > <dependencies > <dependency > <groupId > org.apache.flink</groupId > <artifactId > force-shading</artifactId > <version > 1.8.1</version > </dependency > <dependency > <groupId > com.typesafe.akka</groupId > <artifactId > akka-actor_${scala.binary.version}</artifactId > <version > 2.5.24</version > </dependency > </dependencies > </project >
这里省略了可以配置 scala.binary.version
属性的 profile
部分,我们的意图是根据不同的 profile 来打出适应不同 Scala 版本的 jar 包,这一点可以在 mvn clean install -P<profile-name>
里指定。但是我们看一下在默认 profile 下发出来的 pom 文件的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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" > <modelVersion > 4.0.0</modelVersion > <groupId > moe.tison</groupId > <artifactId > eden_${scala.binary.version}</artifactId > <version > 0.1-SNAPSHOT</version > <properties > <scala.binary.version > 2.12</scala.binary.version > </properties > <dependencies > <dependency > <groupId > org.apache.flink</groupId > <artifactId > force-shading</artifactId > <version > 1.8.1</version > </dependency > <dependency > <groupId > com.typesafe.akka</groupId > <artifactId > akka-actor_${scala.binary.version}</artifactId > <version > 2.5.24</version > </dependency > </dependencies > </project >
可以看到,${scala.binary.version}
的部分并没有被解析。这是因为 MAVEN install 的策略是直接复制工程对象的 pom file 字段对应的文件,在这里它直接复制了项目下的 pom.xml 文件。
这样会有什么问题呢?基于简单的复制策略 MAVEN 并不会解析 pom 文件中的 properties,这会导致我们基于不同的 profile 打出来的包的项目描述 pom 文件都是一样的。即使我们分别为 Scala 2.11 和 2.12 版本打了两个不同的 jar 包,由于 ${scala.binary.version}
未解析,在下游应用中引用的使用属性永远是以 <scala.binary.version>2.12</scala.binary.version>
为准,也就丧失了原本分开打包兼容不同版本的初衷了。
明白了问题以后,我们来看一下 force-shading
是怎么解决这个问题的。
我们先往 pom.xml 中添加一个 artifactSet
exclude
所有依赖的 maven-shade-plugin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-shade-plugin</artifactId > <version > 3.0.0</version > <executions > <execution > <id > shade-eden</id > <phase > package</phase > <goals > <goal > shade</goal > </goals > <configuration > <artifactSet > <excludes > <exclude > org.apache.flink:force-shading</exclude > <exclude > com.typesafe.akka:akka-actor_*</exclude > </excludes > </artifactSet > </configuration > </execution > </executions > </plugin > </plugins > </build >
可以看到打出来的 jar 包的 pom 文件依旧不解析相关的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <?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" > <modelVersion > 4.0.0</modelVersion > <groupId > moe.tison</groupId > <artifactId > eden_${scala.binary.version}</artifactId > <version > 0.1-SNAPSHOT</version > <properties > <scala.binary.version > 2.12</scala.binary.version > </properties > <dependencies > <dependency > <groupId > org.apache.flink</groupId > <artifactId > force-shading</artifactId > <version > 1.8.1</version > </dependency > <dependency > <groupId > com.typesafe.akka</groupId > <artifactId > akka-actor_${scala.binary.version}</artifactId > <version > 2.5.24</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-shade-plugin</artifactId > <version > 3.0.0</version > <executions > <execution > <id > shade-eden</id > <phase > package</phase > <goals > <goal > shade</goal > </goals > <configuration > <artifactSet > <excludes > <exclude > org.apache.flink:force-shading</exclude > <exclude > com.typesafe.akka:akka-actor_*</exclude > </excludes > </artifactSet > </configuration > </execution > </executions > </plugin > </plugins > </build > </project >
我们试着把 force-shading
像 FLINK 那样 inlcude
到最终的 uber-jar 中。
1 2 3 4 5 <artifactSet > <includes > <include > org.apache.flink:force-shading</include > </includes > </artifactSet >
可以看到这次打出来的 jar 包中的 pom 文件已经解析了 properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?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/maven-v4_0_0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > moe.tison</groupId > <artifactId > eden_2.12</artifactId > <version > 0.1-SNAPSHOT</version > <build > <plugins > <plugin > <artifactId > maven-shade-plugin</artifactId > <version > 3.0.0</version > <executions > <execution > <id > shade-eden</id > <phase > package</phase > <goals > <goal > shade</goal > </goals > <configuration > <artifactSet > <includes > <include > org.apache.flink:force-shading</include > </includes > </artifactSet > </configuration > </execution > </executions > </plugin > </plugins > </build > <dependencies > <dependency > <groupId > com.typesafe.akka</groupId > <artifactId > akka-actor_2.12</artifactId > <version > 2.5.24</version > <scope > compile</scope > </dependency > </dependencies > <properties > <scala.binary.version > 2.12</scala.binary.version > </properties > </project >
这样,在不同的 profile 下,MAVEN 会把 properties 的使用点全部替换成运行时的值,打出来的包即依赖运行时的值,这个值可以由 profile 指定,就达到了我们打不同的兼容包的需求了。
那么,为什么使用 force-shading
就能达到这样的效果呢?
我们看到 MAVEN install 的时候的一行日志。
1 [INFO] Installing /path/to/eden/dependency-reduced-pom.xml to /home/user/.m2/repository/moe/tison/eden_2.12/0.1-SNAPSHOT/eden_2.12-0.1-SNAPSHOT.pom
可以看到我们是把 dependency-reduced-pom.xml
作为最终安装时复制的 pom file 来使用的,这个文件由 maven-shade-plugin
在对比模块原有依赖和经过 shade 之后的依赖有区别是解析产生,即 FLINK 中注释提到的 effective pom,它会在运行时基于依赖 diff 产生,由于运行时的 properties 本就是被设定的值,因此它巧合的就完成了这个解析 properties 的任务。
这里,依赖的 diff 由 pom.xml 中依赖 force-shading
而在 uber-jar 中打入 force-shading
因此不含这个依赖来达到。由于这个 diff 出现在 flink-parent
中,所有的子模块都会经历这个过程,所以所有子模块都使用了 effective pom 作为最终的 pom 文件。由于 force-shading
本身是一个空模块,只是为了触发 maven-shade-plugin
,因此打入 uber-jar 中也不会有问题。
此外还有一点值得一提,即 maven-shade-plugin
在不指定 artifactSet
或 artifactSet/includes
为空时,默认是将所有依赖打入 uber-jar,即不选=全选。force-shading
的 include
恰巧避免了这一出乎意料的情况的发生,保证 shade 时所有的 inlcude
和 exclude
都显式声明,客观上也减少了潜在的难以分析的漏洞。
关于 FLINK 和 SPARK 使用 force-shading
手段的讨论:
关于 MAVEN 安装时不解析 properties 的讨论: