• Windows下IntelliJ IDEA中调试Spark Standalone


    研究Spark源码也有一段时间了,一直都是直接看代码,没有调试。虽然带着思路去看源代码已经能够帮助我们去了解Spark了;但是很多细节从字面上是看不出来的,如果我能够通过运行时调试验证我的想法,或者能够查看某个类中变量和结构在运行时是什么岂不是更好?好,我们今天就来实现这个想法。

    动手之前,我已经在网上找了关于spark调试的方法,要么就是local模式的,要么就是写的很模糊。spark local模式和其他分布式模式有很大不同,虽然可以在local模式下进行debug,但有很多东西只有在分布式模式下才有用,本文主要是介绍在Spark Standalone模式下如何调试Driver、Master、Worker和Executor(yarn模式比较复杂,还需要结合yarn的debug模式才能搞定,但研究standalone已经可以搞清楚spark的大部分原理了)。

    一、主要思想

    1、spark-class: 像Master、Worker、Driver都是通过spark-class脚本进行启动各自的jvm(Driver其实是间接由spark-class启动,提交程序时以spark-class启动SparkSubmit,然后SparkSubmit以反射的形式调用Driver的main方法从而实现driver的运行)。 
    spark-class中设置了各jvm的参数,所以可以在这些参数中加入debug的相关参数。 
    2、Executor即CoarseGrainedExecutorBackend的参数在spark-class脚本中修改没用,因为其不是通过spark-class启动的,其是被ExecutorRunner启动,在buildCommandSeq->buildJavaOpts对相应参数进行设置,比如固定MaxPermSize=128m等。可以在SparkConf中设置spark.executor.extraJavaOptions参数。

    二、前提要求

    本文假定你已经掌握或完成了以下内容:

    1、已经完成了一个Spark Standalone的集群(大小不重要,能用就行,不需要hdfs的支持),并且能够顺利启动和运行

    2、IntelliJ IDEA、Scala插件、Java JDK、Scala SDK都已经安装和配置完成

    3、拥有java开发基础 

    三、新建测试项目

    启动IntelliJ IDEA,选择New Project,然后选择Scala,点击下一步

    image

    输入项目名称和参数继续下一步:

    image

    在src目录下新建一个scala类对象:RemoteDebug,我们将用这个类来做测试。

    image

     输入以下代码(相信你看出来了,就是官网的计算π的例子):

    复制代码
    object RemoteDebug {
      def main(args: Array[String]) {
        val conf = new SparkConf().setAppName("Spark Pi").setMaster("spark://Master:7077")
    .setJars(List("F:\Spark\SparkRemoteDebug\out\artifacts\SparkRemoteDebug_jar\SparkRemoteDebug.jar"))
        val spark = new SparkContext(conf)
        val slices = if (args.length > 0) args(0).toInt else 2
        val n = 100000 * slices
        val count = spark.parallelize(1 to n, slices).map { i =>
          val x = random * 2 - 1
          val y = random * 2 - 1
          if (x * x + y * y < 1) 1 else 0
        }.reduce(_ + _)
        println("Pi is roughly " + 4.0 * count / n)
        spark.stop()
      }
    }
    复制代码

    注意以下两点:

    1、”setMaster("spark://Master:7077") ”不能忘,因为我们是要在Standalone集群上运行的,“Master“就是master所在的主机名,如果你没有在本机配置“Master”指向集群中的master机器IP的话,请直接使用IP,如:spark://192.168.1.103:7070。

    2、”setJars(List("F:\Spark\SparkRemoteDebug\out\artifacts\SparkRemoteDebug_jar\SparkRemoteDebug.jar"))“,告诉Spark 集群我们要提交的作业的代码在哪里,也就是我们包含我们程序的Jar包的路径,记住路径中千万别包含中文,不然会出错(血的教训)。

    首先给项目添加Spark的依赖jar以及源码zip,选择项目,按下F4,就会弹出下面的配置窗体:

    image

    接下来配置我们的程序打包:

    imageimage

    imageimage

    好,让我们来看看我们的测试结果。启动Spark集群成功后,你应该可以看到SparkUI界面(以下截图是我的环境):

    image

    为我们的程序添加一个启动项:

    image

    image

    好,点击旁边的绿色小三角启动按钮,启动我们的程序,查看运行结果:

    image

    image

    至此,我们的程序已经能够正常在集群上运行,并返回结果了,下一步我们就来看看怎么调试Driver、Master和Worker以及Executor。 

    三、调试Spark Standalone

    1、修改Master配置。

    首先,我们停止我们的spark Cluster,因为我们需要修改一一些参数,打开Master所在机器的spark-class文件进行编辑,记得先备份哦

    image

    找到并修改为以下内容:

    复制代码
    找到以下内容:
    
    # Master, Worker, and HistoryServer use SPARK_DAEMON_JAVA_OPTS (and specific opts) + SPARK_DAEMON_MEMORY.
      'org.apache.spark.deploy.master.Master')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_MASTER_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.worker.Worker')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_WORKER_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.history.HistoryServer')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_HISTORY_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
    
    
    
    修改为:
    
      # Master, Worker, and HistoryServer use SPARK_DAEMON_JAVA_OPTS (and specific opts) + SPARK_DAEMON_MEMORY.
      'org.apache.spark.deploy.master.Master')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_MASTER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8002,server=y,suspend=n"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.worker.Worker')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_WORKER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8003,server=y,suspend=n"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.history.HistoryServer')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_HISTORY_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
    复制代码

    2、修改Worker配置

    同理(文件位置参考Master机器),找到以下内容并修改:

    复制代码
    找到以下内容:
    
    # Master, Worker, and HistoryServer use SPARK_DAEMON_JAVA_OPTS (and specific opts) + SPARK_DAEMON_MEMORY.
      'org.apache.spark.deploy.master.Master')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_MASTER_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.worker.Worker')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_WORKER_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.history.HistoryServer')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_HISTORY_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
    
    修改为:
    
    PARK_DAEMON_MEMORY.
      'org.apache.spark.deploy.master.Master')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_MASTER_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.worker.Worker')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_WORKER_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8009,server=y,suspend=n"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
      'org.apache.spark.deploy.history.HistoryServer')
        OUR_JAVA_OPTS="$SPARK_DAEMON_JAVA_OPTS $SPARK_HISTORY_OPTS"
        OUR_JAVA_MEM=${SPARK_DAEMON_MEMORY:-$DEFAULT_MEM}
        ;;
    复制代码

    3、重新启动Spark Cluster

    查看Master和Worker的日志

    image

     image

     

    好,可以看到,我们的Master和Worker都已经启动成功,并且按照我们的配置监听各自的端口,下面我们就通过程序来调试它们。 

    4、开启调试Master和Worker

    回到我们的idea中,添加两个Remote启动项

    image

    image

    重要的时刻来了,我们先启动调试Master,并加上属于Master代码的断点:

    image

     可以看到,idea已经连接到了我们Cluster中的Master机器的8002端口,而这正是我们在集群中配置的端口。同理启动Slave1(Worker)

    image

    为了能够调试Executor,我们得修改一下我们前面写的代码,修改后的代码如下:

    复制代码
    def main(args: Array[String]) {
        val conf = new SparkConf().setAppName("Spark Pi").setMaster("spark://Master:7077")
          .setJars(List("F:\Spark\SparkRemoteDebug\out\artifacts\SparkRemoteDebug_jar\SparkRemoteDebug.jar"))
          .set("spark.executor.extraJavaOptions", "-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,suspend=n")
        println("sleep begin.")
        Thread.sleep(10000) //等待10s,让有足够时间启动driver的remote debug
        println("sleep end.")
    
        val spark = new SparkContext(conf)
        val slices = if (args.length > 0) args(0).toInt else 2
        val n = 100000 * slices
        val count = spark.parallelize(1 to n, slices).map { i =>
          val x = random * 2 - 1
          val y = random * 2 - 1
          if (x * x + y * y < 1) 1 else 0
        }.reduce(_ + _)
        println("Pi is roughly " + 4.0 * count / n)
        spark.stop()
      }
    复制代码

    最后,我们来测试一下我们的成果:

    Driver:

    image

    Master:

    image

    Worker:

    image

    Executor(CoarseGrainedExecutorBackend)是运行在Worker上的另一个JVM进程,貌似我这次实验并没有进入断点,等哪天找到方法,再补上。

    仅此而已
  • 相关阅读:
    攀岩
    插入排序
    runtime error
    vector
    旅行家
    九键字母组合
    [蓝桥杯][基础训练]Sine之舞
    代码计算程序运行的时间
    max_element
    distance
  • 原文地址:https://www.cnblogs.com/snowbook/p/5841657.html
Copyright © 2020-2023  润新知