Spark踩坑1—无法初始化Main

上周用Spark开发了一个小程序,打包好submit到yarn后slave却一直报错:

1
java.lang.NoClassDefFoundError: Could not initialize class Main

代码:

Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author liuxinsi
* @mail akalxs@gmail.com
*/
public class Main {
private static final JavaSparkContext sc = new JavaSparkContext(
new SparkConf().setAppName(“Loader”).setMaster(“yarn”)
);
private static final SQLContext sqlContext = new SQLContext(sc);
public static void main(String[]() args) {
DataFrame df = sqlContext.read().json("data.json");
// 一些基本操作,略...
}
}

但开发时在local环境下则一点问题也没有。
一开始怀疑yarn环境里有重复的fatjar导致两个包的Main冲突,后来检查了一遍没问题,yarn本身也是隔离的。
google了一圈基本上都是说序列化或环境的问题。
仔细想了一下当submit到yarn的流程是把Spark的环境和提交的jar打了一个zip包放到HDFS上进行分发,而Spark程序运行时仅在数据混洗shuffle或其他传输情况才会进行序列化,而且无论是jdk的序列化还是kryo的序列化,如果对象不能序列化也是报序列化的异常,不可能连入口类都没初始化就跑到序列化的步骤去了。

环境问题的话也在加了-verbose情况下仔细检查了加载的jar包和顺序,也没发现什么问题。
最后实在没辙只能不停翻官方提供的例子看看有没有什么头绪,找了半天最后突然发现唯一一点不同就是官方的小demo里的所有SparkContext的初始都是放在函数里,也就是都是局部变量,而我一直都是写成全局变量。所以猜想会不会是这个原因导致的,后来把代码改成:

Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author liuxinsi
* @mail akalxs@gmail.com
*/
public class Main {
public static void main(String[]() args) {
JavaSparkContext sc = new JavaSparkContext(
new SparkConf().setAppName(“Loader”).setMaster(“yarn”)
);
SQLContext sqlContext = new SQLContext(sc);
DataFrame df = sqlContext.read().json("data.json");
// 一些基本操作,略...
}
}

打包后提交果然解决,虽然Spark的亲儿子是Scala这种函数式编程语言,但怎么也想不明白一个全局变量为什么会没办法初始化。
还有坑人的一点是本地local[*]模式下也测不出这种问题。