SpringXD HA 配置

SpringXD官方文档 上说的不是很清楚,而且有些配置(如 配置 hadoop namenode ha )并没有在上面说明,只是简单的说明了怎么配置 namenode ,如果没有ha配置,那么在生产环境中会令人头痛。

XD Admin HA

说明

官方文档 中,有说如何配置,就是通过启动多个admin ,然后通过 zookeeper 管理。
Spring XD 要求只有一个主节点来和 Container 交互,例如 Stream 发布等。同时,这些操作都是按顺序处理的。假如只有一个 admin ,那么就存在单点失败的风险,因此,在生产环境中推荐做法是启动 2 个或者多 admin 。注意:在有多个 admin 节点的时候,每个 admin 都可以处理 REST 请求,但是只有一个实例会作为 Leader 处理请求并更新运行时的状态。如果 Leader 宕掉,另一个可用的admin就会成为新的 Leader 来接管任务。当然,Spring XD 的HA不只是他自身要求ha,还需要依赖外部服务,如:zookeeperMessageBus 等 HA 配置。

配置信息

如果要配置 admin 的 ha,那么启动多个 admin 即可,但是请注意,如果是在同一台机器上部署多个admin,需要在启动时候添加如下参数以防止和默认的端口(9393)冲突:
--httpPort 用来指定rest api端口
--mgmtPort 用来指定管理端口
如果在不同机器上启动,只需配置相同的配置文件,然后启动即可。

XD Container HA

当添加 Container 的时候,Spring XD 可以动态水平扩展,也就是说不需要额外什么操作,只需像第一次启动 Container 一样输入命令 bin/xd-container 启动即可。

XD Hadoop namenode HA

如果 xd 中创建 stream 或者其他任务是用到了 hdfs 的功能,那么要配置 hadoopnamenode ,要在 xd/config/servers.xml中的 spring.hadoop.fsUri 的配置项中配置。注意,这个地方只允许配置一个 host,如果有备用 namenode ,是不允许配置在这个地方的。但是这样配置是有问题的,就是存在 hadoopnamenode 主从切换后 xd 的 stream 无法写入 hdfs 或者读取 hdfs 的故障。要解决这个问题,我们要再 xd/config/hadoop.properties 中配置如下配置项:

1
2
3
4
5
dfs.nameservices=MyCluster
dfs.ha.namenodes.MyCluster=nn1,nn2
dfs.namenode.rpc-address.MyCluster.nn2=hadoop-master1-host:8020
dfs.namenode.rpc-address.MyCluster.nn1=hadoop-master2-host:8020
dfs.client.failover.proxy.provider.MyCluster=org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider

其中,配置项中所有的 MyCluster 要换成自己项目中 hadoop 集群的名字,然后在 xd/config/servers.xmlspring.hadoop.fsUri 值配置成 hdfs://MyCluster:8020(注意8020端口换成自己配置的),hadoop-master1-hosthadoop-master1-host 换成自己集群的 hadoop的 master 的主机名字或者ip。这样配置后,重新启动 admincontainer 就会自动检测 hadoop 的 namenode 主从,并自行切换。
如果要在 xd-shell中使用,需要登录shell之后(如果有安全设置,还需要先用密码登录成功),输入一下命令:

1
2
3
4
5
6
hadoop config props set --property dfs.nameservices=MyCluster
hadoop config props set --property dfs.ha.namenodes.MyCluster=nn1,nn2
hadoop config props set --property dfs.namenode.rpc-address.MyCluster.nn1=hadoop-master1-host:8020
hadoop config props set --property dfs.namenode.rpc-address.MyCluster.nn2=hadoop-master2-host:8020
hadoop config props set --property dfs.client.failover.proxy.provider.MyCluster=org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider
hadoop config fs --namenode hdfs://MyCluster

MyClusterhadoop-master1-hosthadoop-master2-host 同上配置。对于 shell 来说,单纯配置 hadoop 的主 namenode也是可以的,因为这个配置只是对 shell 起作用。如果觉得每次打开shell都要输入上面几行配置太繁琐的话,可以将 xd/config/hadoop.properties 中配置的项目添加一份到shell/config/hadoop.properties 即可,这样在shell中操作hdfs,只需配置 hadoop config fs --namenode hdfs://MyCluster:8020 即可。

附言

对于如何在xd中使用hadoop的namenode ha配置,xd 官方文档中并未见说明,而是百般Goole之后得到的结果,而且由于xd资料尚少,搜索结果不佳,最后通过搜索关键词 xd namenode fail 才找到解决方案,请参见这里

sbt源配置

sbt 默认源下载有点慢,我们可以调教它,让它从我们自己配置的源下载。

配置源

.sbt (默认是在用户名下)文件夹中创建 repositories 文件,然后添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[repositories]
local
my: http://o8r69qphn.bkt.clouddn.com/
Nexus osc: http://maven.oschina.net/content/groups/public/
Nexus osc thirdparty: http://maven.oschina.net/content/repositories/thirdparty/
central: http://central.maven.org/maven2/



[ivy]
local
my: http://o8r69qphn.bkt.clouddn.com/
Nexus osc: http://maven.oschina.net/content/groups/public/
Nexus osc thirdparty: http://maven.oschina.net/content/repositories/thirdparty/
central: http://central.maven.org/maven2/
TypeSafe: https://oss.sonatype.org/content/repositories/releases/
#proxy库
typesafe-ivy-releases: http://dl.bintray.com/typesafe/ivy-releases/
typesafe-maven-releases: http://dl.bintray.com/typesafe/maven-releases/
typesafe-sbt-plugin-releases: http://dl.bintray.com/sbt/sbt-plugin-releases/

#group库
ivy-releases : typesafe-ivy-releases,typesafe-sbt-plugin-releases

上面 repositories 是说加载maven镜像中的库文件从这个标签下的路径中找, local 代表从本地中找,默认是 .M2 中,下面的都是自定义源,名字随便取。
下面 ivy 是加载ivy库的。

修改加载配置项

单纯修改上面的源还不足以让sbt加载我们的源。打开 sbt 软件安装位置下的 conf/sbtopts 文件,在其中添加:

1
-Dsbt.override.build.repos=true

然后就可以生效了。

没有深入研究,如果有错还请指出

Linux shell 命令

lsof

适用于ip4

1
lsof -Pnl +M -i4 | grep port

适用于ip6

1
lsof -Pnl +M -i6 | grep port

awk

杀掉名字一样的java进程

1
jps |grep SparkSubmit | awk '{print "kill -9 " $1}' | sh

如果仅仅是打印命令,则后面的管道和sh不需要加,如下

1
jps |grep SparkSubmit | awk '{print "kill -9 " $1}'

find

删除找到的符合条件的文件(或者目录)

1
find . -iname target -exec rm -rf {} \;

sbt-idea-入门及配置

Java 环境配置

这个就不多说了,这是前提条件,请自行安装后配置正确,如果不清楚请自行搜索 java 环境变量配置 相关问题。

Scala 配置

首先要要配置 scala 环境。从官方地址下载,这里我们使用scala2.10.6版本,所以从这里下载对应的平台版本。Windows请下载 scala-2.10.6.zip ,MacOS和Linux请下载 scala-2.10.6.tgz

下载完成后,解压到一个目录,然后配置环境变量 SCALA_HOME ,把scala的解压后的绝对路径配置到 SCALA_HOME ,然后增加 PATH 的配置。以下以 WIndows 和 Linux 举例来说,假如这里解压后得到的文件夹为 scala-2.10.6

Windows 环境

假如 scala-2.10.6 文件夹放在了 C:\目录下,那么新增 SCALA_HOME 的值为 C:\scala-2.10.6 ,然后找到 PATH 这个环境变量, 在已有的值后面添加 ;%SCALA_HOME%\bin (注意前面的分号),重新打开一个新的命令行窗口即可操作。关于windows环境变量的其他说明情自行补脑,这里就不多啰嗦了。

Linux 和 Mac 环境

这里假定我们 scala-2.10.6 的 scala 目录存放在 /usr/local/ 下,绝对路径就是 /usr/local/scala-2.10.6

Bash 环境

bash 环境下可以修改 ~/.bashrc 或者 /etc/profile 文件,添加一下内容:

1
2
3
4
SCALA_HOME=/usr/local/scala-2.10.6
PATH=$PATH:$SCALA_HOME/bin

export SCALA_HOME PATH

然后执行 source ~/.bashrc 或者 source /etc/profile 即可。

Zsh 环境

如果你的终端bash用的是 zsh ,那么需要在 ~/.zshrc 文件中增加上述内容,然后执行 source ~/.zshrc 即可。

Sbt 配置

官方地址从这里下载,如果是MacOS的话,可以依照这里提示的方法进行快捷安装,如果是其他平台或者想手动配置,点击这里直接下载即可。

下载完成并解压,得到文件夹 sbt

Windows 环境

假如我们将 sbt 文件夹放到了 C:\目录下。新增环境变量 SBT_HOME 值为 C:\sbt, 在 PATH 变量值后面添加 ;%SBT_HOME%\bin,重新打开一个新的命令行窗口即可。

Linux 或者 Mac 环境

这里假定我们将 sbt 目录放在了 /usr/local/ 目录下。同上面配置 scala 环境变量一样。

Bash 环境

编辑 ~/.bashrc 或者 /etc/profile ,新增以下内容:

1
2
3
4
SBT_HOME=/usr/local/sbt
PATH=$PATH:$SBT_HOEM/bin

export SBT_HOME PATH

然后执行 source ~/.bashrc 或者 source /etc/profile 即可生效。

Zsh 环境

编辑 ~/.zshrc, 添加上面的内容并保存后,执行 source ~/.zshrc 即可生效。

示例程序

项目结构

在随意一个地方创建一个文件夹,名字为 spark-sbt-demo ,下面是目录结构:

1
2
3
4
5
6
7
8
9
spark-sbt-demo                                                                                                                                                                         
├── build.sbt
├── project
│   ├── build.properties
│   └── plugins.sbt
└── src
├── main
   ├── scala
      └── WordCount.scala

build.sbt 文件中添加如下内容(注意每行要用空行隔开):

1
2
3
4
5
6
7
8
9
10
11
name := "spark-sbt-demo"

version := "1.0"

scalaVersion := "2.10.6"

organization := "spark.demo"

version := "1.0.0-SNAPSHOT"

libraryDependencies += "org.apache.spark" % "spark-core_2.10" % "1.6.1" % "provided"

WordCount.scala 文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.apache.spark.{SparkConf, SparkContext}

/**
* Created by sdvdxl on 16/5/11.
*/

object WordCount {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("spark-sbt-demo").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.textFile("src/main/scala/WordCount.scala").flatMap(_.split(" ")).map(word=>(word,1)).reduceByKey(_+_).foreach(println)
sc.stop()
}
}

build.properties 文件内容 :

1
sbt.version = 0.13.11

plugins.sbt 文件先保持为空。

至此,我们已经创建了一个 sbt 机构的项目。

接下来会说明使用sbt下载依赖,使用 idea 创建 sbt 项目,在idea中如何运行sbt管理的 spark app。

Sbt 的基本使用

上面我们创建了一个用 sbt 管理的 spark app 项目,如果想要提交到spark中运行,那么需要打包成jar包,好在 sbt 本身或者插件提供了这样的功能。

应用打包

打开命令行,切换到该项目目录下,然后输入 sbt 之后,进入 sbt 的交互中,然后输入 package ,开始打包,最后如果看到类似

[info] Done packaging.
[success] Total time: 11 s, completed 2016-5-11 12:32:09

字样,那么说明打包成功,打成的 jar 包在上面的日志中可以找到。

第三方 jar 统一打包

在写应用的时候,我们不只是用到 spark 自身的 jar 包,还会用到好多其他第三方类库,那么,在提交应用到 spark 运行的时候,这些第三方依赖也需要一并提交上去,否则会出现找不到类的问题。如果依赖少的话,直接将这些 jar 包直接一个一个提交上去也没问题,但是一旦依赖了大量的类库,这种方法显然是低效费力的,那么怎么才能将这些所有的第三方依赖打成一个 jar 包呢?

sbt 本身没有提供这样的功能,但是我们可以依靠相应的插件完成此操作。记得上面有个文件内容留空的 plugins.sbt 文件吗?这个文件中可以配置我们想要完成特定功能的插件,现在我们在其中添加如下内容:

1
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.2")

然后重新 进入 sbt 交互式环境,输入 assemblyPackageDependency 回车,稍后将看到类似如下输出:

[info] Done packaging.
[success] Total time: 41 s, completed 2016-5-11 13:36:37

这样就成功的将所有依赖的第三方类库打包到一个 jar 包中了,具体打包的文件可以在上面的日志中看到。

使用 idea 创建 sbt 项目

安装插件

使用 idea 创建 sbt 项目需要安装 scalasbt 插件。
打开idea的首选项,然后找到 Plugins ,点击 Browser repositores... 按钮,输入 scala 搜索,然后找到 scalasbt 的插件进行安装,如下图所示:
scala-sbt-plugins
安装完成后重启idea。

创建 sbt 项目

File -> New -> Project… 打开项目创建向导:
创建sbt项目
创建完成后,等待idea刷新项目,目录结构大体如下(project/project 和 target相关没有列出):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spark-sbt-demo                                                                                                                                                                
├── build.sbt
├── project
│   ├── build.properties
│   └── plugins.sbt
└── src
├── main
│   ├── java
│   ├── resources
│   ├── scala
│   └── scala-2.11
└── test
├── java
├── resources
├── scala
└── scala-2.11
  • plugins.sbt 文件放置插件配置
  • build.sbt 是整体的项目配置信息
  • build.properties 可以设置 sbt 版本
  • java 目录存放 java 文件
  • scala 目录存放 scala 文件
  • resources 目录用来存放配置文件
  • test 相关目录用来存放测试相关文件

    在 idea 中 运行 spark app

    上面我们介绍了如何使用 idea 项目向导创建一个 sbt 项目,现在我们来说一下如何在 idea 中直接运行 sbt 构建的 spark app。

这里我们使用一开始我们创建的那个项目,使用 idea 导入功能,File -> Open 找到项目目录打开即可。
WordCount.scala 文件中右键,选择 Run WordCount ,开始运行,但是结果可能不是我们所期望的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/spark/SparkConf
at WorldCount$.main(WorldCount.scala:8)
at WorldCount.main(WorldCount.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: org.apache.spark.SparkConf
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more

Process finished with exit code 1

这是为什么呢?原因是我们在 build.sbt 中配置的 spark 依赖是这样的:

1
libraryDependencies += "org.apache.spark" % "spark-core_2.10" % "1.6.1" % "provided"

注意到后面的 provided 了吗?这个代表打包或者运行的时候不会将这个 jar 包的文件包含进去(注意:spark app 要求这样,注意不要把spark相关的jar包包含进去)。这样导致我们无法再 idea 中调试或者运行 spark app。

解决方案还是有的,sbt 和 maven(也是一个项目管理的软件)一样,提供了模块开发功能,我们定义两个模块,一个模块就是我们上面我们做好的,另一个是用来运行的,这个里面包含了运行时类库,配置如下:

  1. 创建一个名为 main 的文件夹,把项目中的 src 文件夹移动到这个目录下
  2. 在项目根目录下创建名为 run 的文件夹
  3. 修改项目根目录下的 build.sbt 文件,内容为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    name := "spark-sbt-demo"

    version := "1.0"

    scalaVersion := "2.10.4"

    organization := "spark.demo"

    version := "1.0.0-SNAPSHOT"

    libraryDependencies += "org.apache.spark" % "spark-core_2.10" % "1.6.1" % "provided"

    lazy val root = (project in file(".")).aggregate(main, run)

    lazy val main = (project in file("main"))

    lazy val run = (project in file("run")).dependsOn(main)
  4. 在子项目 main 创建 build.sbt 内容为:

    1
    libraryDependencies += "org.apache.spark" % "spark-core_2.10" % "1.6.1" % "provided"
  5. 在子项目 run 创建 build.sbt 内容为 :

    1
    libraryDependencies += "org.apache.spark" % "spark-core_2.10" % "1.6.1"
  6. 配置运行参数,如下图:
    idea-spark-run-config
    然后选择上面的运行配置,运行即可。这里可能会碰到一个异常:

    1
    2
    3
    4
    Exception in thread "main" org.apache.hadoop.mapred.InvalidInputException: Input path does not exist: file:/Users/du/workspace/hekr/spark-sbt-demo/src/main/scala/WorldCount.scala
    at org.apache.hadoop.mapred.FileInputFormat.listStatus(FileInputFormat.java:251)
    at org.apache.hadoop.mapred.FileInputFormat.getSplits(FileInputFormat.java:270)
    ......

这是由于上面我们修改了改程序main文件的位置,导致找不到该文件所致,请自行设置为一个存在的文件路径或者修改为 main/src/main/scala/WorldCount.scala 重新运行即可成功。

Sbt 本地依赖库存储位置配置

抽空再补上,其实就是建立一个连接,先自行思考方案。

项目下载

没有源码下载的都是耍流氓,点这里下载

JDK多版本管理

在实际工作环境中经常碰到不同项目要用不同jdk版本问题,比如我的项目组现在用的是jdk8,而用spark打包的应用是用的jdk7,所以有必要记录一下版本配置和切换问题。

sparkApp提交到SpringXD出现错误可能情况及解决方法

编写spark代码:

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
package com.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import scala.Tuple2;

import java.util.ArrayList;
import java.util.List;

/**
* Created by sdvdxl on 2016/3/14.
*/

public class SparkCalcDemo {
private static final String HADOOP_URL = "hdfs://10.10.1.110:8020/";

public static void main(String[] args) throws Exception {
SparkConf conf = new SparkConf().setAppName("test").setMaster("local[1]");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> textFile = sc.textFile(HADOOP_URL + "/xd/dataset1/2016/03/14/15/01", 1);
JavaRDD<String> words = textFile.flatMap(new FlatMapFunction<String, String>() {
public Iterable<String> call(String s) {
List<String> list = new ArrayList<String>();

JSONObject jobj = JSON.parseObject(new String(org.apache.commons.codec.binary.Base64.decodeBase64(s.substring(1, s.length() - 1))));
list.add(jobj.getString("name"));
list.add(jobj.getString("random"));
return list;
}
});
JavaPairRDD<String, Integer> pairs = words.mapToPair(new PairFunction<String, String, Integer>() {
public Tuple2<String, Integer> call(String s) {
return new Tuple2<String, Integer>(s, 1);
}
});
JavaPairRDD<String, Integer> counts = pairs.reduceByKey(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer a, Integer b) {
return a + b;
}
});

counts.foreach(tuple2 ->
System.out.println(tuple2._1 + " : " + tuple2._2));
}
}

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<?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>kafka-demo</groupId>
<artifactId>kafka-demo</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>1.6.1</version>
<!--<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.10</artifactId>
</exclusion>

</exclusions>-->

</dependency>
<!--dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.10</artifactId>
<version>2.7.2</version>
</dependency>-->

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>

<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-base64</artifactId>
<version>2.16.2</version>
</dependency>


</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive> <!-- 表示是否不包含间接依赖的包 -->
<stripVersion>false</stripVersion> <!-- 去除版本信息 -->
</configuration>

<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- 拷贝项目依赖包到lib/目录下 -->
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
</configuration>
</execution>
</executions>
</plugin>

<!-- 项目资源插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<!-- 拷贝项目src/main/resources/下,除.bat以外的所有文件到conf/目录下 -->
<outputDirectory>${project.build.directory}/conf</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<excludes>
<exclude>*.bat</exclude>
</excludes>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-command</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<!-- 只拷贝项目src/main/resources/目录下的.bat文件到输出目录下 -->
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
<includes>
<include>*.bat</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>

<!-- 打包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<!-- 生成MANIFEST.MF的设置 -->
<manifest>
<!-- 为依赖包添加路径, 这些路径会写在MANIFEST文件的Class-Path下 -->
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<!-- jar启动入口类-->
<mainClass>com.some.package.some.class.Main</mainClass>
</manifest>
<manifestEntries>
<!-- 在Class-Path下添加配置文件的路径 -->
<Class-Path>conf/</Class-Path>
</manifestEntries>
</archive>
<includes>
<!-- 打jar包时,只打包class文件 -->
<include>**/*.class</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>oschina</id>
<url>http://maven.oschina.net/content/groups/public</url>
</repository>
<repository>
<id>mavenspring</id>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>jcenter</id>
<url>http://jcenter.bintray.com</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-release</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/libs-release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>

</project>

定义xd job:

1
job create --name sparkAppDemo --definition "sparkapp --mainClass=com.demo.SparkCalcDemo --appJar=/home/spark/spark-app.jar --master=local[1]" --deploy

加载job

1
job launch sparkAppDemo

然后出现以下类似的错误,主要是:...redis:queue-inbound-channel-adapter...错误

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
2016-03-16T10:16:10+0800 1.3.1.RELEASE INFO DeploymentSupervisor-0 zk.ZKJobDeploymentHandler - Deployment status for job 'sparkAppDemo': DeploymentStatus{state=deployed}
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark application 'com.demo.SparkCalcDemo' finished with exit code: 1
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:284)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:238)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.util.jar.JarVerifier.processEntry(JarVerifier.java:316)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.util.jar.JarVerifier.update(JarVerifier.java:228)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.util.jar.JarFile.initializeVerifier(JarFile.java:383)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.util.jar.JarFile.getInputStream(JarFile.java:450)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.JarIndex.getJarIndex(JarIndex.java:137)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath$JarLoader$1.run(URLClassPath.java:839)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath$JarLoader$1.run(URLClassPath.java:831)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.security.AccessController.doPrivileged(Native Method)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath$JarLoader.ensureOpen(URLClassPath.java:830)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath$JarLoader.<init>(URLClassPath.java:803)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath$3.run(URLClassPath.java:530)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath$3.run(URLClassPath.java:520)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.security.AccessController.doPrivileged(Native Method)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath.getLoader(URLClassPath.java:519)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath.getLoader(URLClassPath.java:492)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath.getNextLoader(URLClassPath.java:457)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at sun.misc.URLClassPath.getResource(URLClassPath.java:211)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.net.URLClassLoader$1.run(URLClassLoader.java:365)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.security.AccessController.doPrivileged(Native Method)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.lang.Class.forName0(Native Method)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at java.lang.Class.forName(Class.java:348)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:538)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:166)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:189)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:110)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
2016-03-16T10:16:14+0800 1.3.1.RELEASE ERROR inbound.job:sparkAppDemo-redis:queue-inbound-channel-adapter1 tasklet.SparkTasklet - Spark Logger: Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties

仔细看的话,在上面有一句Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes错误,这个错误是由于导出的jar包结构信息不正确导致的。用eclipse的导出runnable jar 功能导出的jar包就没问题了。

另外如果有依赖的jar包没哟被加载进去,则会在最上方出现java.lang.NoClassDefFoundError:类似信息。

相关资料:以分布式方式运行Spring-XD

golang打印错误栈信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package mainimport (
"runtime"
"fmt")func main() {
outer()}func outer() {
inner()}func inner() {

defer func() {
if err := recover(); err != nil {
trace := make([]byte, 1024)
count := runtime.Stack(trace, true)
fmt.Printf("Recover from panic: %s\n", err)
fmt.Printf("Stack of %d bytes: %s\n", count, trace)
}
}()

panic("Fake error!")}
,