Spark 新手一名,同样也是 Scala 菜鸟,由于对这两个都不是特别熟悉,所以希望能在 IDE中 coding 和 debug,但是调试 Spark 程序和往常接触过的不一样,并且有一些观念上的错误,总结一下。

坑一:网络代理

程序写好以后,可以直接 debug 的(只限于 spark.master= local[*]的场景),由于使用了 ShadowSocks 全局代理翻墙,最初一直报错,Google 了好大会儿也没找到问原因,后来才猛的想起代理还开着,而 hosts 文件中恰恰没有 localhost 映射到127.0.0.1中,修改 hosts,解决。直接 debug 可以使用较小的数据进行测试,不是非得网上众多教程那样得 sbt package -> spark-submit -> Remote Debug 那样不方便。

坑二:worksheet 运行?

虽然 spark-shell也提供了交互式命令行,尝试代码非常方便,不用每次都 debug 启动 sprak,那样效率太低。然而保存代码就比较麻烦了,因此想着能不能使用 scala worksheet来运行,这样结果也即时可见,代码也不会丢失,尝试了一番,发现不行,有高手解释为:spark 的 REPL 解释器和 Scala 的不一样,因此里边是运行不了 spark 程序的。难道就没有其他办法了吗?有的,采用Scala Console 代替,在文件上右键点击,选择 Run Scala Console 即可,可以与把文件里的代码发送给 console 运行,虽然不如 worksheet 方便,但也好过 spark-sheel 或者每次都启动 debug了。

坑三:debug on spark cluster

这个和坑一有些类似,在老板的三台机器上搭建了一个小集群,并以 standlone cluster 方式运行,于是就直接在 cluster 上debug 吧,获取 sparkContext 的方式如代码所示

1
val spark = SparkSession.builder().master("spark://192.168.6.131:7077").appName("bigjoy").getOrCreate()

在控制台中的 Log 如下,不断地停掉和开启 Executor,心想也不至于吧,数据量没那么大呀!

1
WARN TaskSchedulerImpl: Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient resources

于是去检查 spark UI 的 log, 看到下面的错误

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
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1713)
at org.apache.spark.deploy.SparkHadoopUtil.runAsSparkUser(SparkHadoopUtil.scala:70)
at org.apache.spark.executor.CoarseGrainedExecutorBackend$.run(CoarseGrainedExecutorBackend.scala:174)
at org.apache.spark.executor.CoarseGrainedExecutorBackend$.main(CoarseGrainedExecutorBackend.scala:270)
at org.apache.spark.executor.CoarseGrainedExecutorBackend.main(CoarseGrainedExecutorBackend.scala)
Caused by: org.apache.spark.SparkException: Exception thrown in awaitResult
at org.apache.spark.rpc.RpcTimeout$$anonfun$1.applyOrElse(RpcTimeout.scala:77)
at org.apache.spark.rpc.RpcTimeout$$anonfun$1.applyOrElse(RpcTimeout.scala:75)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
at org.apache.spark.rpc.RpcTimeout$$anonfun$addMessageIfTimeout$1.applyOrElse(RpcTimeout.scala:59)
at org.apache.spark.rpc.RpcTimeout$$anonfun$addMessageIfTimeout$1.applyOrElse(RpcTimeout.scala:59)
at scala.PartialFunction$OrElse.apply(PartialFunction.scala:167)
at org.apache.spark.rpc.RpcTimeout.awaitResult(RpcTimeout.scala:83)
at org.apache.spark.rpc.RpcEnv.setupEndpointRefByURI(RpcEnv.scala:88)
at org.apache.spark.executor.CoarseGrainedExecutorBackend$$anonfun$run$1.apply$mcV$sp(CoarseGrainedExecutorBackend.scala:188)
at org.apache.spark.deploy.SparkHadoopUtil$$anon$1.run(SparkHadoopUtil.scala:71)
at org.apache.spark.deploy.SparkHadoopUtil$$anon$1.run(SparkHadoopUtil.scala:70)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:422)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1698)
... 4 more
Caused by: java.io.IOException: Failed to connect to /192.168.1.105:51340
at org.apache.spark.network.client.TransportClientFactory.createClient(TransportClientFactory.java:228)
at org.apache.spark.network.client.TransportClientFactory.createClient(TransportClientFactory.java:179)
at org.apache.spark.rpc.netty.NettyRpcEnv.createClient(NettyRpcEnv.scala:197)
at org.apache.spark.rpc.netty.Outbox$$anon$1.call(Outbox.scala:191)
at org.apache.spark.rpc.netty.Outbox$$anon$1.call(Outbox.scala:187)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.NoRouteToHostException: No route to host: /192.168.1.105:51340
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:224)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:289)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
... 1 more

看到 IP:192.168.1.105 后猛然想起,个人使用的笔记本网络连接的是办公室的路由器,而办公室的路由器的 ip 才是和集群的机器 Ip 在同一局域网中,中间跨了级!办公室的路由器又没有设置端口映射,难怪找不到!改为使用单位的无线路由(和集群一个局域网)后就没有此错误了!或者也可以设置一下小路由器的端口映射!

坑四:sbt 或 maven 中的依赖版本(包括小版本)一定要与集群一致

使用坑三中的 debug on cluster, 由于很早建立的 maven 工程,采用的是 org.apache.spark:spark-core_2.1:2.0.1依赖,后来搭建集群的时候2.0.2版本已经发布,所以采用了最新的,由于大版本一致,所以就没有在意,debug 的时候报以下错误:

1
java.io.InvalidClassException: org.apache.spark.executor.TaskMetrics; local class incompatible: stream classdesc serialVersionUID = -6966587383730940799, local class serialVersionUID = -2231953621568687904

此问题由小版本不一致导致,说来也正常,向下兼容很正常,但向上的,呵呵,所以保持一致吧!

坑五:找不到类?

由于工程采用 maven 构建,我使用了 phoenix 的依赖,当 spark.master=local[*]的时候,调试没有任何问题,但是当把 spark.master 设置为 spark://spark-master:7077也就是采用集群的时候,会提示除 spark 自带的(core, sql,mllib, stream)库之外,其他的都提示找不到,解决方案除了 1)参考网上的远程调试外 ,现提供另一种方式, 2)类似于 Hadoop的调试方式

1.设置 artifact

File -> Project Structre, 在 Artifacts 里边新建一个 jar 包,选择主类,在 Output Layout 中可以删除 spark 相关的(因为集群中已经有了,其他集群中CASSPATH 包含的都可以省掉,减少 jar包体积),最后确认即可。



2.spark 添加 jar 依赖

只需要个 sparkContext 添加 jar包即可,代码如下

1
2
3
4
 val spark = SparkSession.builder().master("spark://192.168.6.131:7077").appName("bigjoy").getOrCreate()

//注意路径是编译后的路径
spark.sparkContext.addJar("/Users/dq/IdeaProjects/subject/out/artifacts/analysis_jar/analysis.jar")

在debug 前,先 Build -> Build Artifacts -> xxx.jar,把代码中的路径替换为实际的路径,然后就可以像其他普通的代码一样调试了,不过发现运行的比较慢,因为没有与远程调试的进行对比,所以哪个更好一些就暂不能下结论了,不过这个免去了上传 jar 包、spark-submit、remote debug 等过程,简单一些。

就先总结这么多吧!