BigData-Notes/notes/大数据应用常用打包方式.md

307 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 大数据应用常用打包方式
<nav>
<a href="#一简介">一、简介</a><br/>
<a href="#二mvn-package">二、mvn package</a><br/>
<a href="#三maven-assembly-plugin插件">三、maven-assembly-plugin插件</a><br/>
<a href="#四maven-shade-plugin插件">四、maven-shade-plugin插件</a><br/>
<a href="#五其他打包需求">五、其他打包需求</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#1-使用非Maven仓库中的Jar">1. 使用非Maven仓库中的Jar</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#2-排除集群中已经存在的Jar">2. 排除集群中已经存在的Jar</a><br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#3-打包Scala文件">3. 打包Scala文件</a><br/>
</nav>
## 一、简介
在提交大数据作业到集群上运行时,通常需要先将项目打成 JAR 包。这里以 Maven 为例,常用打包方式如下:
- 不加任何插件,直接使用 mvn package 打包;
- 使用 maven-assembly-plugin 插件;
- 使用 maven-shade-plugin 插件;
- 使用 maven-jar-plugin 和 maven-dependency-plugin 插件;
以下分别进行详细的说明。
## 二、mvn package
不在 POM 中配置任何插件,直接使用 `mvn package` 进行项目打包,这对于没有使用外部依赖包的项目是可行的。但如果项目中使用了第三方 JAR 包,就会出现问题,因为 `mvn package` 打的 JAR 包中是不含有依赖包,会导致作业运行时出现找不到第三方依赖的异常。这种方式局限性比较大,因为实际的项目往往很复杂,通常都会依赖第三方 JAR。
大数据框架的开发者也考虑到这个问题,所以基本所有的框架都支持在提交作业时使用 `--jars` 指定第三方依赖包,但是这种方式的问题同样很明显,就是你必须保持生产环境与开发环境中的所有 JAR 包版本一致,这是有维护成本的。
基于上面这些原因,最简单的是采用 `All In One` 的打包方式,把所有依赖都打包到一个 JAR 文件中,此时对环境的依赖性最小。要实现这个目的,可以使用 Maven 提供的 `maven-assembly-plugin``maven-shade-plugin` 插件。
## 三、maven-assembly-plugin插件
`Assembly` 插件支持将项目的所有依赖、文件都打包到同一个输出文件中。目前支持输出以下文件类型:
- zip
- tar
- tar.gz (or tgz)
- tar.bz2 (or tbz2)
- tar.snappy
- tar.xz (or txz)
- jar
- dir
- war
### 3.1 基本使用
在 POM.xml 中引入插件,指定打包格式的配置文件 `assembly.xml`(名称可自定义),并指定作业的主入口类:
```xml
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>com.heibaiying.wordcount.ClusterWordCountApp</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
```
assembly.xml 文件内容如下:
```xml
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0
http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>jar-with-dependencies</id>
<!--指明打包方式-->
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>true</unpack>
<scope>runtime</scope>
<!--这里以排除 storm 环境中已经提供的 storm-core 为例,演示排除 Jar 包-->
<excludes>
<exclude>org.apache.storm:storm-core</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>
```
### 3.2 打包命令
采用 maven-assembly-plugin 进行打包时命令如下:
```shell
# mvn assembly:assembly
```
打包后会同时生成两个 JAR 包,其中后缀为 `jar-with-dependencies` 是含有第三方依赖的 JAR 包,后缀是由 `assembly.xml``<id>` 标签指定的,可以自定义修改。
<div align="center"> <img src="../pictures/storm-jar.png"/> </div>
## 四、maven-shade-plugin插件
`maven-shade-plugin``maven-assembly-plugin` 功能更为强大,比如你的工程依赖很多的 JAR 包,而被依赖的 JAR 又会依赖其他的 JAR 包,这样,当工程中依赖到不同的版本的 JAR 时,并且 JAR 中具有相同名称的资源文件时shade 插件会尝试将所有资源文件打包在一起时,而不是和 assembly 一样执行覆盖操作。
**通常使用 `maven-shade-plugin` 就能够完成大多数的打包需求,其配置简单且适用性最广,因此建议优先使用此方式。**
### 4.1 基本配置
采用 `maven-shade-plugin` 进行打包时候,配置示例如下:
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.sf</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.dsa</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.rsa</exclude>
<exclude>META-INF/*.EC</exclude>
<exclude>META-INF/*.ec</exclude>
<exclude>META-INF/MSFTSIG.SF</exclude>
<exclude>META-INF/MSFTSIG.RSA</exclude>
</excludes>
</filter>
</filters>
<artifactSet>
<excludes>
<exclude>org.apache.storm:storm-core</exclude>
</excludes>
</artifactSet>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
```
以上配置来源于 Storm Github在上面的配置中排除了部分文件这是因为有些 JAR 包生成时,会使用 jarsigner 生成文件签名 (完成性校验),分为两个文件存放在 META-INF 目录下:
- a signature file, with a .SF extension
- a signature block file, with a .DSA, .RSA, or .EC extension。
如果某些包的存在重复引用,这可能会导致在打包时候出现 `Invalid signature file digest for Manifest main attributes` 异常,所以在配置中排除这些文件。
### 4.2 打包命令
使用 maven-shade-plugin 进行打包的时候,打包命令和普通打包一样:
```shell
# mvn package
```
打包后会生成两个 JAR 包,提交到服务器集群时使用非 original 开头的 JAR。
<div align="center"> <img src="../pictures/storm-jar2.png"/> </div>
## 五、其他打包需求
### 1. 使用非Maven仓库中的Jar
通常上面两种打包能够满足大多数的使用场景。但是如果你想把某些没有被 Maven 管理 JAR 包打入到最终的 JAR 中,比如你在 `resources/lib` 下引入的其他非 Maven 仓库中的 JAR此时可以使用 `maven-jar-plugin``maven-dependency-plugin` 插件将其打入最终的 JAR 中。
```xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<!--指定 resources/lib 目录-->
<classpathPrefix>lib/</classpathPrefix>
<!--应用的主入口类-->
<mainClass>com.heibaiying.BigDataApp</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>compile</phase>
<goals>
<!--将 resources/lib 目录所有 Jar 包打进最终的依赖中-->
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!--将 resources/lib 目录所有 Jar 包一并拷贝到输出目录的 lib 目录下-->
<outputDirectory>
${project.build.directory}/lib
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
### 2. 排除集群中已经存在的Jar
通常为了避免冲突,官方文档都会建议你排除集群中已经提供的 JAR 包,如下:
Spark 官方文档 Submitting Applications 章节:
> When creating assembly jars, list Spark and Hadoop as `provided` dependencies; these need not be bundled since they are provided by the cluster manager at runtime.
Strom 官方文档 Running Topologies on a Production Cluster 章节:
>Then run mvn assembly:assembly to get an appropriately packaged jar. Make sure you exclude the Storm jars since the cluster already has Storm on the classpath.
按照以上说明,排除 JAR 包的方式主要有两种:
+ 对需要排除的依赖添加 `<scope>provided</scope>` 标签,此时该 JAR 包会被排除,但是不建议使用这种方式,因为此时你在本地运行也无法使用该 JAR 包;
+ 建议直接在 `maven-assembly-plugin``maven-shade-plugin` 的配置文件中使用 `<exclude>` 进行排除。
### 3. 打包Scala文件
如果你使用到 Scala 语言进行编程,此时需要特别注意 :默认情况下 Maven 是不会把 `scala` 文件打入最终的 JAR 中,需要额外添加 `maven-scala-plugin` 插件,常用配置如下:
```xml
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.15.1</version>
<executions>
<execution>
<id>scala-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<includes>
<include>**/*.scala</include>
</includes>
</configuration>
</execution>
<execution>
<id>scala-test-compile</id>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
```
## 参考资料
关于 Maven 各个插件的详细配置可以查看其官方文档:
+ maven-assembly-plugin : http://maven.apache.org/plugins/maven-assembly-plugin/
+ maven-shade-plugin : http://maven.apache.org/plugins/maven-shade-plugin/
+ maven-jar-plugin : http://maven.apache.org/plugins/maven-jar-plugin/
+ maven-dependency-plugin : http://maven.apache.org/components/plugins/maven-dependency-plugin/
关于 maven-shade-plugin 的更多配置也可以参考该博客: [maven-shade-plugin 入门指南](https://www.jianshu.com/p/7a0e20b30401)