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進程,貌似我這次實驗並沒有進入斷點,等哪天找到方法,再補上。

僅此而已


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM