Windows下IntelliJ IDEA中調試Spark Standalone

參考:http://dataknocker.github.io/2014/11/12/idea%E4%B8%8Adebug-spark-standalone/html

轉載請註明來自:http://www.cnblogs.com/yuananyun/p/4265706.html                       java

研究Spark源碼也有一段時間了,一直都是直接看代碼,沒有調試。雖然帶着思路去看源代碼已經可以幫助咱們去了解Spark了;可是不少細節從字面上是看不出來的,若是我可以經過運行時調試驗證個人想法,或者可以查看某個類中變量和結構在運行時是什麼豈不是更好?好,咱們今天就來實現這個想法。git

動手以前,我已經在網上找了關於spark調試的方法,要麼就是local模式的,要麼就是寫的很模糊。spark local模式和其餘分佈式模式有很大不一樣,雖然能夠在local模式下進行debug,但有不少東西只有在分佈式模式下才有用,本文主要是介紹在Spark Standalone模式下如何調試Driver、Master、Worker和Executor(yarn模式比較複雜,還須要結合yarn的debug模式才能搞定,但研究standalone已經能夠搞清楚spark的大部分原理了)。github

1、主要思想

一、spark-class: 像Master、Worker、Driver都是經過spark-class腳本進行啓動各自的jvm(Driver實際上是間接由spark-class啓動,提交程序時以spark-class啓動SparkSubmit,而後SparkSubmit以反射的形式調用Driver的main方法從而實現driver的運行)。
spark-class中設置了各jvm的參數,因此能夠在這些參數中加入debug的相關參數。
二、Executor即CoarseGrainedExecutorBackend的參數在spark-class腳本中修改沒用,由於其不是經過spark-class啓動的,其是被ExecutorRunner啓動,在buildCommandSeq->buildJavaOpts對相應參數進行設置,好比固定MaxPermSize=128m等。能夠在SparkConf中設置spark.executor.extraJavaOptions參數。apache

 

2、前提要求

本文假定你已經掌握或完成了如下內容:dom

一、已經完成了一個Spark Standalone的集羣(大小不重要,能用就行,不須要hdfs的支持),而且可以順利啓動和運行jvm

二、IntelliJ IDEA、Scala插件、Java JDK、Scala SDK都已經安裝和配置完成socket

三、擁有java開發基礎 分佈式

3、新建測試項目

啓動IntelliJ IDEA,選擇New Project,而後選擇Scala,點擊下一步ide

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()
  }
}

注意如下兩點:

一、」setMaster("spark://Master:7077") 」不能忘,由於咱們是要在Standalone集羣上運行的,「Master「就是master所在的主機名,若是你沒有在本機配置「Master」指向集羣中的master機器IP的話,請直接使用IP,如:spark://192.168.1.103:7070。

二、」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。 

3、調試Spark Standalone

一、修改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}
    ;;

 

二、修改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}
    ;;

 

三、從新啓動Spark Cluster

查看Master和Worker的日誌

image

 

 

 

 

 

 

 

 

 

 

 

 image

 

 

 

 

 

 

 

 

 

 

 

 

 

 

好,能夠看到,咱們的Master和Worker都已經啓動成功,而且按照咱們的配置監聽各自的端口,下面咱們就經過程序來調試它們。 

四、開啓調試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進程,貌似我此次實驗並無進入斷點,等哪天找到方法,再補上。

相關文章
相關標籤/搜索