在將 Storm Topology 提交到服務器集羣運行時,須要先將項目進行打包。本文主要對比分析各類打包方式,並將打包過程當中須要注意的事項進行說明。主要打包方式有如下三種:html
如下分別進行詳細的說明。java
不在 POM 中配置任何插件,直接使用 mvn package
進行項目打包,這對於沒有使用外部依賴包的項目是可行的。git
但若是項目中使用了第三方 JAR 包,就會出現問題,由於 mvn package
打包後的 JAR 中是不含有依賴包的,若是此時你提交到服務器上運行,就會出現找不到第三方依賴的異常。github
若是你想採用這種方式進行打包,可是又使用了第三方 JAR,有沒有解決辦法?答案是有的,這一點在官方文檔的Command Line Client 章節有所講解,主要解決辦法以下。redis
在使用 storm jar
提交 Topology 時,可使用以下方式指定第三方依賴:shell
--jars
指定;--artifacts
指定,此時若是想要排除某些依賴,可使用 ^
符號。指定後 Storm 會自動到中央倉庫進行下載,而後緩存到本地;--artifactRepositories
指明倉庫地址,庫名和地址使用 ^
符號分隔。如下是一個包含上面三種狀況的命令示例:apache
./bin/storm jar example/storm-starter/storm-starter-topologies-*.jar \ org.apache.storm.starter.RollingTopWords blobstore-remote2 remote \ --jars "./external/storm-redis/storm-redis-1.1.0.jar,./external/storm-kafka/storm-kafka-1.1.0.jar" \ --artifacts "redis.clients:jedis:2.9.0,org.apache.kafka:kafka_2.10:0.8.2.2^org.slf4j:slf4j-log4j12" \ --artifactRepositories "jboss-repository^http://repository.jboss.com/maven2, \ HDPRepo^http://repo.hortonworks.com/content/groups/public/"
這種方式是創建在你可以鏈接到外網的狀況下,若是你的服務器不能鏈接外網,或者你但願能把項目直接打包成一個 ALL IN ONE
的 JAR,即包含全部相關依賴,此時能夠採用下面介紹的兩個插件。緩存
maven-assembly-plugin 是官方文檔中介紹的打包方法,來源於官方文檔:Running Topologies on a Production Cluster服務器
If you're using Maven, the Maven Assembly Plugin can do the packaging for you. Just add this to your pom.xml:app
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.path.to.main.Class</mainClass> </manifest> </archive> </configuration> </plugin>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.
官方文檔主要說明了如下幾點:
<mainClass>
標籤指定主入口類;<descriptorRef>
標籤指定打包相關配置。jar-with-dependencies
是 Maven預約義 的一種最基本的打包配置,其 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> </dependencySet> </dependencySets> </assembly>
咱們能夠經過對該配置文件進行拓展,從而實現更多的功能,好比排除指定的 JAR 等。使用示例以下:
在 POM.xml 中引入插件,並指定打包格式的配置文件爲 assembly.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
拓展自 jar-with-dependencies.xml
,使用了 <excludes>
標籤排除 Storm jars,具體內容以下:
<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--> <excludes> <exclude>org.apache.storm:storm-core</exclude> </excludes> </dependencySet> </dependencySets> </assembly>
在配置文件中不只能夠排除依賴,還能夠排除指定的文件,更多的配置規則能夠參考官方文檔:Descriptor Format
採用 maven-assembly-plugin 進行打包時命令以下:
# mvn assembly:assembly
打包後會同時生成兩個 JAR 包,其中後綴爲 jar-with-dependencies
是含有第三方依賴的 JAR 包,後綴是由 assembly.xml
中 <id>
標籤指定的,能夠自定義修改。提交該 JAR 到集羣環境便可直接使用。
第三種方式是使用 maven-shade-plugin,既然已經有了 maven-assembly-plugin,爲何還須要 maven-shade-plugin,這一點在官方文檔中也是有所說明的,來自於官方對 HDFS 整合講解的章節Storm HDFS Integration,原文以下:
When packaging your topology, it's important that you use the maven-shade-plugin as opposed to the maven-assembly-plugin.
The shade plugin provides facilities for merging JAR manifest entries, which the hadoop client leverages for URL scheme resolution.
If you experience errors such as the following:
java.lang.RuntimeException: Error preparing HdfsBolt: No FileSystem for scheme: hdfsit's an indication that your topology jar file isn't packaged properly.
If you are using maven to create your topology jar, you should use the following
maven-shade-plugin
configuration to create your topology jar。
這裏第一句就說的比較清晰,在集成 HDFS 時候,你必須使用 maven-shade-plugin 來代替 maven-assembly-plugin,不然會拋出 RuntimeException 異常。
採用 maven-shade-plugin 打包有不少好處,好比你的工程依賴不少的 JAR 包,而被依賴的 JAR 又會依賴其餘的 JAR 包,這樣,當工程中依賴到不一樣的版本的 JAR 時,而且 JAR 中具備相同名稱的資源文件時,shade 插件會嘗試將全部資源文件打包在一塊兒時,而不是和 assembly 同樣執行覆蓋操做。
採用 maven-shade-plugin
進行打包時候,配置示例以下:
<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 目錄下:
若是某些包的存在重複引用,這可能會致使在打包時候出現 Invalid signature file digest for Manifest main attributes
異常,因此在配置中排除這些文件。
使用 maven-shade-plugin 進行打包的時候,打包命令和普通的同樣:
# mvn package
打包後會生成兩個 JAR 包,提交到服務器集羣時使用 非 original
開頭的 JAR。
經過以上三種打包方式的詳細介紹,這裏給出最後的結論:建議使用 maven-shade-plugin 插件進行打包,由於其通用性最強,操做最簡單,而且 Storm Github 中全部examples 都是採用該方式進行打包。
不管採用任何打包方式,都必須排除集羣環境中已經提供的 storm jars。這裏比較典型的是 storm-core,其在安裝目錄的 lib 目錄下已經存在。
若是你不排除 storm-core,一般會拋出下面的異常:
Caused by: java.lang.RuntimeException: java.io.IOException: Found multiple defaults.yaml resources. You're probably bundling the Storm jars with your topology jar. [jar:file:/usr/app/apache-storm-1.2.2/lib/storm-core-1.2.2.jar!/defaults.yaml, jar:file:/usr/appjar/storm-hdfs-integration-1.0.jar!/defaults.yaml] at org.apache.storm.utils.Utils.findAndReadConfigFile(Utils.java:384) at org.apache.storm.utils.Utils.readDefaultConfig(Utils.java:428) at org.apache.storm.utils.Utils.readStormConfig(Utils.java:464) at org.apache.storm.utils.Utils.<clinit>(Utils.java:178) ... 39 more
關於 maven-shade-plugin 的更多配置能夠參考: maven-shade-plugin 入門指南
更多大數據系列文章能夠參見 GitHub 開源項目: 大數據入門指南