1. 程式人生 > >基於Spark SQL 讀寫Oracle 的簡單案例分析常見問題

基於Spark SQL 讀寫Oracle 的簡單案例分析常見問題

1      概述

本文主要內容包含Spark SQL讀寫Oracle表資料的簡單案例,並針對案例中比較常見的幾個問題給出解決方法。

最後從常見的java.lang.ClassNotFoundException(無法找到驅動類)的異常問題出發,分析相關的幾種解決方法,以及各個解決方法之間的異同點。

2      案例中比較常見問題及其解決方法

2.1     啟動

首先檢視Spark 官網給出的SparkSQL的程式設計指南部分(http://spark.apache.org/docs/latest/sql-programming-guide.html)的JDBC To Other Databases 內容。參考命令:

SPARK_CLASSPATH=postgresql-9.3-1102-jdbc41.jar bin/spark-shell

對應寫出訪問 Oracle的命令,如下:

SPARK_CLASSPATH=$SPARK_HOME/ojdbc14.jar  bin/spark-shell --master local

其中,CLASSPATH相關內容會在後一章節給出詳細分析,在此僅針對其他一些常見問題給出解決方法。

啟動過程如下(部分字串已經被替換,如:$SPARK_HOME):

[[email protected] spark-1.5.2-bin-hadoop2.6]$ SPARK_CLASSPATH=$SPARK_HOME/lib/ojdbc14.jar bin/spark-shell --master local

……

Welcome to

      ____              __

     / __/__  ___ _____/ /__

    _\ \/ _ \/ _ `/ __/  '_/

   /___/ .__/\_,_/_/ /_/\_\   version 1.5.2

      /_/

Using Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71)

Type in expressions to have them evaluated.

Type :help for more information.

16/04/18 11:56:35 INFO spark.SparkContext: Running Spark version 1.5.2

16/04/18 11:56:35 WARN spark.SparkConf:

SPARK_CLASSPATH was detected (set to '$SPARK_HOME/lib/ojdbc14.jar').

This is deprecated in Spark 1.0+.

Please instead use:

- ./spark-submit with --driver-class-path to augment the driver classpath

 - spark.executor.extraClassPath to augment the executor classpath

16/04/18 11:56:35 WARN spark.SparkConf: Setting 'spark.executor.extraClassPath' to '$SPARK_HOME/lib/ojdbc14.jar' as a work-around.

16/04/18 11:56:35 WARN spark.SparkConf: Setting 'spark.driver.extraClassPath' to '$SPARK_HOME/lib/ojdbc14.jar' as a work-around.

……

16/04/18 11:56:51 INFO server.AbstractConnector: Started [email protected]:4040

16/04/18 11:56:51 INFO util.Utils: Successfully started service 'SparkUI' on port 4040.

16/04/18 11:56:51 INFO ui.SparkUI: Started SparkUI at http://192.168.149.86:4040

……

16/04/18 11:56:57 INFO repl.SparkILoop: Created sql context (with Hive support)..

SQL context available as sqlContext.

scala>

下面給出簡單讀寫案例中的常見問題及其解決方法。

2.2     訪問表資料時報找不到合適的驅動

scala> val url = "jdbc:oracle:thin:@xx.xx.xx.xx:1521:dbsid"

url: String = jdbc:oracle:thin:@xx.xx.xx.xx:1521:dbsid

// 通過format指定訪問jdbc,通過options指定訪問時的引數,然後load載入資料

scala> val jdbcDF = sqlContext.read.format("jdbc").options( Map( "url" -> url, "dbtable" -> "MyTable", "user" -> "username", "password" -> " password ")).load()

java.sql.SQLException: No suitable driver found for jdbc:oracle:thin:@xx.xx.xx.xx:1521:dbsid

        at java.sql.DriverManager.getConnection(DriverManager.java:596)

        at java.sql.DriverManager.getConnection(DriverManager.java:187)

        at org.apache.spark.sql.execution.datasources.jdbc.JDBCRDD$$anonfun$getConnector$1.apply(JDBCRDD.scala:188)

        at org.apache.spark.sql.execution.datasources.jdbc.JDBCRDD$$anonfun$getConnector$1.apply(JDBCRDD.scala:181)

        at org.apache.spark.sql.execution.datasources.jdbc.JDBCRDD$.resolveTable(JDBCRDD.scala:121)

        at org.apache.spark.sql.execution.datasources.jdbc.JDBCRelation.<init>(JDBCRelation.scala:91)

        at org.apache.spark.sql.execution.datasources.jdbc.DefaultSource.createRelation(DefaultSource.scala:60)

        at org.apache.spark.sql.execution.datasources.ResolvedDataSource$.apply(ResolvedDataSource.scala:125)

        at org.apache.spark.sql.DataFrameReader.load(DataFrameReader.scala:114)

……

報錯資訊為:

java.sql.SQLException: No suitable driver found for jdbc:oracle:thin:@xx.xx.xx.xx:1521:dbsid

即報錯提示無法找到合適的驅動時,此時可以通過在options方法中指定”driver”屬性為具體的驅動類來解決,如下所示:

scala> val jdbcDF = sqlContext.read.format("jdbc").options( Map( "url" -> url, "dbtable" -> "TEST_TABLE", "user" -> "userName", "password" -> "password", "driver" -> "oracle.jdbc.driver.OracleDriver")).load()

jdbcDF: org.apache.spark.sql.DataFrame = [ID: decimal(0,-127), XX: string, XX: string, XX: string, XX: decimal(0,-127)]

注意:在1.5版本中,當Oracle表字段為Number時,對應DataType為decimal,此時會由於scala的精度斷言丟擲異常——可以在stackoverflow網站查詢該異常——應該是個bug,在1.6中應該已經解決。有興趣可以試下下。——如果繼續show資料的話,會丟擲該異常。

補充:手動載入驅動類也可以解決。

2.3     訪問表資料時報無效呼叫引數

另外,當屬性名寫錯時的錯誤資訊:

——由於在通常寫jdbc的連線時,使用username標識使用者名稱,(比如在sqoop工具中),在SparkSQL中需要使用user,否則會報以下錯誤(可以在官網或API的描述中查詢相關屬性名):

scala> val jdbcDF = sqlContext.read.format("jdbc").options( Map( "url" -> url, "dbtable" -> "TEST_TABLE", "username" -> "userName", "password" -> "password")).load()

java.sql.SQLException: invalid arguments in call

        at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)

        at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)

        at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)

        at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:236)

當出現invalid arguments in call錯誤,表示呼叫時使用了無效引數,可以檢查options中的引數名是否寫錯,尤其是user是否寫成了username。

下面解析最常見的無法找到驅動類的問題及其解決方法。

3      無法找到無法找到驅動類的問題及其解決方法

擴充套件知識:JVM的類載入機制——該知識是解決這一類問題的根本方法。

下面的解決方法主要從JVM程序的classpath配置出發去分析。

在Spark框架中,為JVM程序的classpath配置提供了以下幾種方式:

1.        使用環境變數SPARK_CLASSPATH:將jar包放置在類查詢路徑,比如前面的SPARK_CLASSPATH,設定該變數後可以檢視4040(預設應用監控埠)的環境設定,此時需要在driver和executor的classpath的路徑中放置所需的jar包。

由前面啟動過程中的日誌可知,這種方式已經廢棄,對應的方式是直接設定driver和executor的classpath相關的配置屬性。

2.        使用配置屬性分別設定driver和executor執行時載入jar包的路徑。

a)        spark.executor.extraClassPath

b)       spark.driver.extraClassPath

取代使用環境變數進行配置的方法。由於通常情況下,driver和executor的程序不在同一個節點上,因此分別給出各自的配置屬性。

和前面使用環境變數進行配置的方式一樣,在設定相應的classpath路徑之後,需要將對應的jar包部署到各個節點上的該classpath路徑下。

說明:上述兩種方法都是通過設定JVM程序的classpath屬性,為程序提供jar包查詢路徑的。而對應的jar包,需要人為地部署到各個節點的對應路徑下。

補充:另外可以通過提交命令的命令列選項--driver-class-path來設定driver端程序的classpath,原理類似,因此不作為單獨一種。

注意:--conf命令項方式設定時,--confPROP=VALUE的等號中間不要加空格….

3.        除了前面兩種方式外,Spark框架還提供了第三種方法,可以通過提交命令(spark-submit或spark-shell)的命令列選項--jars,自動上傳、下載所需jar包,並同時將下載的jar包放入JVM程序的classpath路徑中。

說明:通過該命令列選項設定的jar包,會通過http服務(注意,該服務在driver端啟動後才會啟動)上傳jar包,並在executor端執行時下載該jar包,然後放入classpath。即,此時不需要手動部署到各個節點上,但每個提交的應用都會增加jar包上傳、下載的網路IO、磁碟IO開銷。

下面針對提交時的--master選項、--deploymode的選項分別進行解析。

不同選項的簡單說明如下:

1.       當--master選項為local時,對應為in-process方式,此時僅一個程序(local-cluster時也對應一個程序,內部通過例項模擬),因此,對應的classpath實際上使用的都是driver短對應程序的classpath。即只需要配置driver的classpath即可。

2.       當--master選項為叢集的MasterURL(本文主要基於Standalone模式的叢集)時,對應driver和executor是以不同的程序方式啟動,因此需要分別進行設定。並且,在不同的部署模式(--deploymode)下也會有細節上的差異(本質上還是根據JVM類載入機制):

a)        --deploymode為client時:driver程序在當前提交節點上啟動

b)       --deploymode為cluster時:driver程序提交到叢集中,由叢集排程Master負責分配節點,並在分配的節點上啟動driver程序。

說明:不同的部署模式和jar包的上傳、下載有關,即在使用--jars方式時會有所差異,其關鍵點在於,jar包的上傳、下載是通過driver程序啟動過程中啟動的http服務來完成的,當指定的jar包是以本地檔案系統的路徑提供時,在另一個節點啟動的driver程序中的http服務根據該路徑上傳jar包時,也會根據本地檔案系統指定的路徑去上傳,所以此時必須保證由Master節點分配給driver的節點,對應的該路徑上也存在需要上傳的jar包。

因此,建議在使用cluster部署模式提交應用程式時,所使用的路徑儘可能與節點無關,比如使用hdfs檔案系統的路徑等。

3.1    測試類的設計

關於下面使用的SparkTest.jar中的com.TestClass測試類,分別在driver端和executor端同時訪問Oracle的表資料,即兩處都需要載入Oracle的驅動器類。

通過這種方式,方便分別測試driver端和executor端與各自的classpath配置及其jar包放置等相關的內容。

3.1.1       包含Driver與Executor執行邏輯的程式碼

如下所示:

object TestJarwithOracle {

  // 硬編碼

  val url = "jdbc:oracle:thin:@xx.xx.xx.xx:1521:dbsid"

  val user = "userName"

  val password = "password"

  val dbtable = "TABLE_TEST"

  def main(args: Array[String]) {

    val conf = new SparkConf().setAppName(this.getClass.getSimpleName)

    val sc = new SparkContext(conf)

    val sqlContext = new SQLContext(sc)

    val logRDD = sc.parallelize( List( ("name", "action", "date", 1), ("name", "action", "date", 2) ))

    // 處理邏輯:在Driver端

    deleteRecodes("date")

    // 處理邏輯:位於Executor端

    logRDD.foreachPartition(insertInto)

    sc.stop()

  }

  def deleteRecodes(date: String): Unit = {

    var conn: Connection = null

    var ps: PreparedStatement = null

    val sql = s"delete from $dbtable where log_date in ('$date')"

    println(sql)

    try {

      Class.forName("oracle.jdbc.driver.OracleDriver")

      conn = DriverManager.getConnection(url, user, password)

      ps = conn.prepareStatement(sql)

      ps.executeUpdate()

    } catch {

      case e: Exception => e.printStackTrace

    } finally {

      if (ps != null) {

        ps.close()

      }

      if (conn != null) {

        conn.close()

      }

    }

  }

  def insertInto(iterator: Iterator[(String, String, String, Int)]): Unit = {

    var conn: Connection = null

    var ps: PreparedStatement = null

    val sql = s"insert into $dbtable( USER_ACTION, USER_NAME, LOG_DATE, ACTION_CNT) values (?, ?, ?, ?)"

    try {

      //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/spark", "root", "123456")

      Class.forName("oracle.jdbc.driver.OracleDriver")

      conn = DriverManager.getConnection(url, user, password)

      iterator.foreach(data => {

        ps = conn.prepareStatement(sql)

        ps.setString(1, data._1)

        ps.setString(2, data._2)

        ps.setString(3, data._3)

        ps.setInt(4, data._4)

        // ps.setInt(3, data._3)

        ps.executeUpdate()

      }

      )

    } catch {

      case e: Exception => e.printStackTrace

    } finally {

      if (ps != null) {

        ps.close()

      }

      if (conn != null) {

        conn.close()

      }

    }

  }

}

3.1.2       程式碼說明

前面給出了從oracle(其他資料庫基本一樣)中讀取的簡單案例,對應insert的話,和普通oracle表的insert類似,需要注意的是連線的建立位置(可以參考Spark官網的流部分)。

大致原理簡單描述如下:

1.        Driver端建立的物件需要序列化到Executor端才能使用,當特定物件(如資料庫連線)與具體節點繫結(如hostname繫結)時,即使序列化成功,在Executor端反序列化後物件也不能使用(比如反序列化時的初始化失敗或hostname不同導致連線等無法使用等)

2.        一個分割槽對應一個task,Executor上的執行單位為task。

在一個執行單位中複用,即針對分割槽提供一個連線——可以複用連線池。

比如:rdd.foreachPartition(insertInto)

對應的function :insertInto,和普通的資料庫insert方式是一樣的(可以採用批量插入),針對每個分割槽Partition,然後獲取或構建一個數據庫連線,通過該連線將分割槽的Iterator資料插入到資料庫表。

3.2     master為local時

首先檢視下SparkSubmit提交應用程式時,一些不支援的組合形式,對應程式碼如下所示:

private[deploy] def prepareSubmitEnvironment(args: SparkSubmitArguments)

……

case (LOCAL, CLUSTER) =>
  printErrorAndExit("Cluster deploy mode is not compatible with master \"local\"")

,,,,,,

即,在使用local與local-cluster這種local方式時,不支援以CLUSTER的部署模式提交應用程式。

因此以下對應都是在CLIENT的部署模式提交應用程式。

3.2.1       環境變數方式

1.        命令:

2.        說明:

指定SPARK_CLASSPATH時,相當於同時指定driver和executor的classpath,根據前面的分析,實際上local模式下只需要設定driver端的classpath即可,同時需要手動在該路徑下方式所需的jar包,否則會丟擲驅動類無法找到的異常。

參考前面啟動章節的啟動日誌中的紅色斜體部分,表示的是SPARK_CLASSPATH已經被廢棄,建議使用斜體部分的對應配置屬性進行替換。

Please instead use:

- ./spark-submit with --driver-class-path to augment the driver classpath

 - spark.executor.extraClassPath to augment the executor classpath

16/04/18 11:56:35 WARN spark.SparkConf: Setting 'spark.executor.extraClassPath' to '$SPARK_HOME/lib/ojdbc14.jar' as a work-around.

16/04/18 11:56:35 WARN spark.SparkConf: Setting 'spark.driver.extraClassPath' to '$SPARK_HOME/lib/ojdbc14.jar' as a work-around.

3.        測試方式一:在環境變數的路徑下不存在所需jar包,driver和executor端載入類同時異常,執行命令及其異常日誌如下所示:

[[email protected] spark-1.5.2-bin-hadoop2.6]$ SPARK_CLASSPATH=$SPARK_HOME/ojdbc14.jar $SPARK_HOME/bin/spark-submit  --master local \

>   --deploy-mode client \

>   --driver-memory 2g \

>   --driver-cores 1 \

>   --total-executor-cores 2 \

>   --executor-memory 4g \

>   --conf "spark.ui.port"=4081 \

> --class com.mb.TestJarwithOracle \

> /tmp/test/Spark15.jar

16/04/26 10:31:39 INFO spark.SparkContext: Running Spark version 1.5.2

16/04/26 10:31:40 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

16/04/26 10:31:40 WARN spark.SparkConf:

SPARK_CLASSPATH was detected (set to '/ojdbc14.jar').

This is deprecated in Spark 1.0+.

Please instead use:

 - ./spark-submit with --driver-class-path to augment the driver classpath

 - spark.executor.extraClassPath to augment the executor classpath

16/04/26 10:31:40 WARN spark.SparkConf: Setting 'spark.executor.extraClassPath' to '/ojdbc14.jar' as a work-around.

16/04/26 10:31:40 WARN spark.SparkConf: Setting 'spark.driver.extraClassPath' to '/ojdbc14.jar' as a work-around.

16/04/26 10:31:40 INFO spark.SecurityManager: Changing view acls to: hdfs

16/04/26 10:31:40 INFO spark.SecurityManager: Changing modify acls to: hdfs

16/04/26 10:31:40 INFO spark.SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users with view permissions: Set(hdfs); users with modify permissions: Set(hdfs)

16/04/26 10:31:41 INFO slf4j.Slf4jLogger: Slf4jLogger started

16/04/26 10:31:41 INFO Remoting: Starting remoting

16/04/26 10:31:42 INFO Remoting: Remoting started; listening on addresses :[akka.tcp://[email protected]:32898]

16/04/26 10:31:42 INFO util.Utils: Successfully started service 'sparkDriver' on port 32898.

16/04/26 10:31:42 INFO spark.SparkEnv: Registering MapOutputTracker

16/04/26 10:31:42 INFO spark.SparkEnv: Registering BlockManagerMaster

16/04/26 10:31:42 INFO storage.DiskBlockManager: Created local directory at /tmp/blockmgr-43894e1f-4546-477c-91f9-766179306112

16/04/26 10:31:42 INFO storage.MemoryStore: MemoryStore started with capacity 1060.3 MB

16/04/26 10:31:42 INFO spark.HttpFileServer: HTTP File server directory is /tmp/spark-0294727b-0b57-48ff-9f36-f441fa3604aa/httpd-cec03f6e-ca66-49b8-94b9-39427a86ed65

16/04/26 10:31:42 INFO spark.HttpServer: Starting HTTP Server

16/04/26 10:31:42 INFO server.Server: jetty-8.y.z-SNAPSHOT

16/04/26 10:31:42 INFO server.AbstractConnector: Started [email protected]:57115

16/04/26 10:31:42 INFO util.Utils: Successfully started service 'HTTP file server' on port 57115.

16/04/26 10:31:42 INFO spark.SparkEnv: Registering OutputCommitCoordinator

16/04/26 10:31:57 INFO server.Server: jetty-8.y.z-SNAPSHOT

16/04/26 10:31:57 INFO server.AbstractConnector: Started [email protected]:4081

16/04/26 10:31:57 INFO util.Utils: Successfully started service 'SparkUI' on port 4081.

16/04/26 10:31:57 INFO ui.SparkUI: Started SparkUI at http://192.168.149.86:4081

16/04/26 10:31:57 INFO spark.SparkContext: Added JAR file:/tmp/test/Spark15.jar at http://192.168.149.86:57115/jars/Spark15.jar with timestamp 1461637917682

16/04/26 10:31:57 WARN metrics.MetricsSystem: Using default name DAGScheduler for source because spark.app.id is not set.

16/04/26 10:31:57 INFO executor.Executor: Starting executor ID driver on host localhost

16/04/26 10:31:58 INFO util.Utils: Successfully started service 'org.apache.spark.network.netty.NettyBlockTransferService' on port 23561.

16/04/26 10:31:58 INFO netty.NettyBlockTransferService: Server created on 23561

16/04/26 10:31:58 INFO storage.BlockManagerMaster: Trying to register BlockManager

16/04/26 10:31:58 INFO storage.BlockManagerMasterEndpoint: Registering block manager localhost:23561 with 1060.3 MB RAM, BlockManagerId(driver, localhost, 23561)

16/04/26 10:31:58 INFO storage.BlockManagerMaster: Registered BlockManager

16/04/26 10:31:59 INFO scheduler.EventLoggingListener: Logging events to hdfs://nodemaster:8020/user/hdfs/sparklogs/local-1461637917735

delete from TEST_TABLE where log_date in ('date')

java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)

        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)

        at java.security.AccessController.doPrivileged(Native Method)

        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)

        at java.lang.Class.forName0(Native Method)

        at java.lang.Class.forName(Class.java:191)

        at com.mb.TestJarwithOracle$.deleteRecodes(TestJarwithOracle.scala:38)

        at com.mb.TestJarwithOracle$.main(TestJarwithOracle.scala:27)

        at com.mb.TestJarwithOracle.main(TestJarwithOracle.scala)

        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

        at java.lang.reflect.Method.invoke(Method.java:606)

        at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:674)

        at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:180)

        at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:205)

        at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:120)

        at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)

16/04/26 10:31:59 INFO spark.SparkContext: Starting job: foreachPartition at TestJarwithOracle.scala:28

16/04/26 10:31:59 INFO scheduler.DAGScheduler: Got job 0 (foreachPartition at TestJarwithOracle.scala:28) with 1 output partitions

16/04/26 10:31:59 INFO scheduler.DAGScheduler: Final stage: ResultStage 0(foreachPartition at TestJarwithOracle.scala:28)

16/04/26 10:31:59 INFO scheduler.DAGScheduler: Parents of final stage: List()

16/04/26 10:31:59 INFO scheduler.DAGScheduler: Missing parents: List()

16/04/26 10:32:00 INFO scheduler.DAGScheduler: Submitting ResultStage 0 (ParallelCollectionRDD[0] at parallelize at TestJarwithOracle.scala:26), which has no missing parents

16/04/26 10:32:00 INFO storage.MemoryStore: ensureFreeSpace(1200) called with curMem=0, maxMem=1111794647

16/04/26 10:32:00 INFO storage.MemoryStore: Block broadcast_0 stored as values in memory (estimated size 1200.0 B, free 1060.3 MB)

16/04/26 10:32:00 INFO storage.MemoryStore: ensureFreeSpace(851) called with curMem=1200, maxMem=1111794647

16/04/26 10:32:00 INFO storage.MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 851.0 B, free 1060.3 MB)

16/04/26 10:32:00 INFO storage.BlockManagerInfo: Added broadcast_0_piece0 in memory on localhost:23561 (size: 851.0 B, free: 1060.3 MB)

16/04/26 10:32:00 INFO spark.SparkContext: Created broadcast 0 from broadcast at DAGScheduler.scala:861

16/04/26 10:32:00 INFO scheduler.DAGScheduler: Submitting 1 missing tasks from ResultStage 0 (ParallelCollectionRDD[0] at parallelize at TestJarwithOracle.scala:26)

16/04/26 10:32:00 INFO scheduler.TaskSchedulerImpl: Adding task set 0.0 with 1 tasks

16/04/26 10:32:00 INFO scheduler.TaskSetManager: Starting task 0.0 in stage 0.0 (TID 0, localhost, PROCESS_LOCAL, 2310 bytes)

16/04/26 10:32:00 INFO executor.Executor: Running task 0.0 in stage 0.0 (TID 0)

16/04/26 10:32:00 INFO executor.Executor: Fetching http://192.168.149.86:57115/jars/Spark15.jar with timestamp 1461637917682

16/04/26 10:32:00 INFO util.Utils: Fetching http://192.168.149.86:57115/jars/Spark15.jar to /tmp/spark-0294727b-0b57-48ff-9f36-f441fa3604aa/userFiles-9b936a62-13aa-4ac4-8c26-caabe7bd4367/fetchFileTemp8857984169855770119.tmp

16/04/26 10:32:00 INFO executor.Executor: Adding file:/tmp/spark-0294727b-0b57-48ff-9f36-f441fa3604aa/userFiles-9b936a62-13aa-4ac4-8c26-caabe7bd4367/Spark15.jar to class loader

java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)

        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)

        at java.security.AccessController.doPrivileged(Native Method)

        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)

        at java.lang.Class.forName0(Native Method)

        at java.lang.Class.forName(Class.java:191)

        at com.mb.TestJarwithOracle$.insertInto(TestJarwithOracle.scala:61)

        at com.mb.TestJarwithOracle$$anonfun$main$1.apply(TestJarwithOracle.scala:28)

        at com.mb.TestJarwithOracle$$anonfun$main$1.apply(TestJarwithOracle.scala:28)

        at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:902)

        at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:902)

        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1850)

        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1850)

        at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66)

        at org.apache.spark.scheduler.Task.run(Task.scala:88)

        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:214)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)

16/04/26 10:32:00 INFO executor.Executor: Finished task 0.0 in stage 0.0 (TID 0). 915 bytes result sent to driver

16/04/26 10:32:00 INFO scheduler.TaskSetManager: Finished task 0.0 in stage 0.0 (TID 0) in 235 ms on localhost (1/1)

兩個異常分別對應driver端和executor端。

4.        測試方式二:在環境變數的路徑下存在所需jar包,driver和executor端載入類正常。

環境變數同時設定了driver端和executor端的classpath,只要該路徑下有jar包,驅動類即可載入。

下面通過配置屬性分別進行配置並測試。

3.2.2       配置屬性方式

1.        使用配置屬性提交應用程式的命令:

$SPARK_HOME/bin/spark-submit  --master spark://masternode:7078 \[h1] 

           --conf "spark.executor.extraClassPath"="$SPARK_HOME/lib/ojdbc14.jar" \

           --conf "spark.driver.extraClassPath"="$SPARK_HOME/lib/ojdbc14.jar"   \

           --conf "spark.ui.port"=4061 \

           --class com.TestClass \

           /tmp/test/SparkTest.jar

通過前面的分析,在local下,只"spark.driver.extraClassPath"有效。

2.        說明:

此時,配置屬性只是指定jar包在classpath指定的路徑下,但沒有手動將所需的jar包部署到該路徑下,因此載入驅動器類時會丟擲java.lang.ClassNotFoundException:oracle.jdbc.driver.OracleDriver的異常。

3.        執行日誌如下所[h2] :

16/04/14 14:23:18 INFO spark.MapOutputTrackerMaster: Size of output statuses for shuffle 0 is 154 bytes

16/04/14 14:23:19 WARN scheduler.TaskSetManager: Lost task 0.0 in stage 1.0 (TID 2, 192.168.149.98): java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)

        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)

        at java.security.AccessController.doPrivileged(Native Method)

        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)

        at java.lang.Class.forName0(Native Method)

        at java.lang.Class.forName(Class.java:191)

        at com.TestClass$.insertInto(WebLogExtractor.scala:48)

        at com.TestClass$$anonfun$main$1.apply(WebLogExtractor.scala:43)

        at com.TestClass$$anonfun$main$1.apply(WebLogExtractor.scala:43)

        at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:902)

        at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:902)

        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1850)

        at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1850)

        at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66)

        at org.apache.spark.scheduler.Task.run(Task.scala:88)

        at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:214)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)

4.        解決方法:

將對應ojdbc14.jar拷貝到當前節點的"spark.driver.extraClassPath"路徑下即可。

5.        擴充套件:

可以嘗試"spark.executor.extraClassPath"與"spark.driver.extraClassPath"是否設定的幾種情況分別進行測試,以驗證前面針對local時,僅"spark.driver.extraClassPath"有效的分析。

再次總結:只要設定"spark.driver.extraClassPath",並在該路徑下放置了jar包即可。

3.2.3       自動上傳jar包方式

當部署模式為CLIENT時,應用程式(對應Driver)會將childMainClass設定為傳入的mainClass,然後啟動JVM程序,對應程式碼如下所示:

if (deployMode == CLIENT) {

  childMainClass = args.mainClass

  if (isUserJar(args.primaryResource)) {

    childClasspath += args.primaryResource

  }

  if (args.jars != null) { childClasspath ++= args.jars.split(",") }

  if (args.childArgs != null) { childArgs ++= args.childArgs }

}

在client模式,直接啟動應用程式的主類,同時,將主類的jar包和新增的jars包(如果在引數中設定的話)都新增到執行時的classpath中。即當前的driver的classpath會自動包含--jars 設定的jar包。

同時,driver通過啟動的http服務上傳該jar包,executor在執行時下載該jar包,同時放置到executor程序的classpath路徑。

測試案例的構建:刪除前面的環境變數或兩個配置屬性的設定,直接用--jars命令列選項指定所需的第三方jar包(即這裡的驅動類jar包)即可。例如:

$SPARK_HOME/bin/spark-submit  --master local \

  --deploy-mode client \

  --driver-memory 2g \

  --driver-cores 1 \

  --total-executor-cores 2 \

  --executor-memory 4g \

  --conf "spark.ui.port"=4081 \

         --class com.mb.TestJarwithOracle \

         --jars "$SPARK_HOME/thirdlib/ojdbc14.jar" \

         /tmp/test/Spark15.jar    

此時會直--jars指定的jar包加入classpath路徑,因此可以成功載入驅動類。

當將應用程式提交到叢集中時,對應不同的部署模式(--deploy-mode)會有不同的情況,因此下面分別針對不同的部署模式進行分析。

3.3     master為叢集的MasterURL+部署模式為Client時

Client部署模式時,在提交點啟動應用程式,因此對應driver端也在提交節點。此時,"spark.driver.extraClassPath"路徑對應提交節點的路徑。Executor則由排程分配到其他執行節點,此時"spark.executor.extraClassPath"對應的路徑應是針對實際分配執行executor的節點(不是提交節點!)。

3.3.1       配置屬性方式

屬於:叢集中jar包部署+ 配置屬性的方式

通過前面的測試與分析,應該可以知道配置屬性的方式只是將所需jar包放入類載入時查詢的路徑中,而對應的jar包需要人為去部署。

對應在分散式叢集環境下,相關的JVM程序可能在各個節點上啟動,包括driver和executor程序。因此當某個節點啟動某類程序時,需要保證已經手動在該節點上,對應於配置屬性所設定的路徑下,已經存在或部署了所需的jar包。

進一步地,通常由資源排程器負責分配節點,執行程序,因此為了保證分配的節點上的程序能成功載入所需類,應該在叢集的所有節點上部署所需jar包。

優點:一次部署多次使用

缺點:jar包衝突

3.3.1.1    測試1

1.        啟動命令:

         $SPARK_HOME/bin/spark-submit  --master spark://masternode:7078 \

           --conf "spark.executor.extraClassPath"="$SPARK_HOME/thirdlib/ojdbc14.jar" \

           --conf "spark.driver.extraClassPath"="$SPARK_HOME/thirdlib/ojdbc14.jar"   \

           --conf "spark.ui.port"=4071 \

           --class com.mb.TestJarwithOracle \

           /tmp/test/SparkTest.jar      

2.        測試說明:

a)        通過--conf命令列選項,設定Driver與Executor端的classpath配置屬性。

b)       在Driver端的classpath路徑下放置所需的jar包。

c)        在Executor端的classpath路徑下刪除所需的jar包。

補充:這裡放置或刪除jar包,可以簡單通過修改對應配置屬性的路徑來模擬。

3.        測試結果:

a)        Driver端與Oracle的操作:由於設定了classpath路徑,同時該路徑下放置了所需的jar包,因此操作成功,直接檢視driver的終端輸出日誌。

b)       Executor端與Oracle的操作:雖然設定了classpath路徑,但該路徑下沒有放置所需的jar包,因此操作失敗,錯誤資訊如下所示(檢視4040-預設埠-的executor頁面,找到對應的stderr日誌資訊):

3.3.1.2    測試2

1.        啟動命令:

           --conf "spark.executor.extraClassPath"="$SPARK_HOME/thirdlib/ojdbc14.jar" \

           --conf "spark.driver.extraClassPath"="$SPARK_HOME/thirdlib/ojdbc14.jar"   \

           --conf "spark.ui.port"=4071 \

           --class com.mb.TestJarwithOracle \

           /tmp/test/SparkTest.jar      

2.        測試說明:

a)        通過--conf命令列選項,設定Driver與Executor端的classpath配置屬性。

b)       在Executor端的classpath路徑下放置所需的jar包。

c)        在Driver端的classpath路徑下刪除所需的jar包。

3.        測試結果:

a)        Executor端與Oracle的操作:由於設定了classpath路徑,同時該路徑下放置了所需的jar包,因此操作成功。

b)       Driver端與Oracle的操作:雖然設定了classpath路徑,但該路徑下沒有放置所需的jar包,因此操作失敗,直接檢視終端的錯誤資訊,如下所示:

java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)

        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)

        at java.security.AccessController.doPrivileged(Native Method)

        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)

        at java.lang.Class.forName0(Native Method)

        at java.lang.Class.forName(Class.java:191)

        at com.mb.TestJarwithOracle$.deleteRecodes(TestJarwithOracle.scala:38)

        at com.mb.TestJarwithOracle$.main(TestJarwithOracle.scala:27)

        at com.mb.TestJarwithOracle.main(TestJarwithOracle.scala)

        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

        at java.lang.reflect.Method.invoke(Method.java:606)

        at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:674)

        at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:180)

        at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:205)

        at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:120)

        at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)

補充說明:Driver端捕捉了異常,因此Executor可以繼續執行。

3.3.1.3    擴充套件1

在classpath對應的這兩個配置屬性中,使用不同路徑的結果是不同的,比如前面測試案例中的配置屬性對應路徑修改為hdfs檔案系統路徑時,driver端的驅動類載入會丟擲異常,命令如下:

$SPARK_HOME/bin/spark-submit  --master spark://nodemaster:7078 \

  --deploy-mode client \

  --driver-memory 2g \

  --driver-cores 1 \

  --total-executor-cores 2 \

  --executor-memory 4g \

  --conf "spark.ui.port"=4081 \

  --conf "spark.executor.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" \

  --conf "spark.driver.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar"   \

         --class com.mb.TestJarwithOracle \

         /tmp/test/Spark15.jar    

簡單理解:

1.       Driver端classpath相關的配置:在啟動應用程式(Driver)時作為JVM的Options使用,此時只能識別本地路徑,使用hdfs檔案系統路徑的話,無法識別,因此類載入會失敗。

2.       Executor端classpath相關的配置:會根據指定的路徑去下載jar包,hdfs等檔案系統以及被封裝,因此可以下載到本地——對應預設在work路徑的app目錄中,然後新增到僅的classpath路徑下,因此可以識別hdfs等檔案系統的路徑。

3.3.1.4    擴充套件2

由於之前有人提過幾次類似的問題, 再此順便給出簡單說明。

異常日誌如下所示:

16/04/26 11:35:58 ERROR util.SparkUncaughtExceptionHandler: Uncaught exception in thread Thread[appclient-registration-retry-thread,5,main]

java.util.concurrent.RejectedExecutionException: Task [email protected] rejected from [email protected][Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]

        at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)

        at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)

        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)

        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:110)

        at org.apache.spark.deploy.client.AppClient$ClientEndpoint$$anonfun$tryRegisterAllMasters$1.apply(AppClient.scala:96)

        at org.apache.spark.deploy.client.AppClient$ClientEndpoint$$anonfun$tryRegisterAllMasters$1.apply(AppClient.scala:95)

        at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)

        at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)

        at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)

        at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:108)

        at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)

        at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:108)

        at org.apache.spark.deploy.client.AppClient$ClientEndpoint.tryRegisterAllMasters(AppClient.scala:95)

        at org.apache.spark.deploy.client.AppClient$ClientEndpoint.org$apache$spark$deploy$client$AppClient$ClientEndpoint$$registerWithMaster(AppClient.scala:121)

        at org.apache.spark.deploy.client.AppClient$ClientEndpoint$$anon$2$$anonfun$run$1.apply$mcV$sp(AppClient.scala:132)

        at org.apache.spark.util.Utils$.tryOrExit(Utils.scala:1119)

        at org.apache.spark.deploy.client.AppClient$ClientEndpoint$$anon$2.run(AppClient.scala:124)

        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)

        at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)

        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)

        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)

16/04/26 11:35:58 INFO storage.DiskBlockManager: Shutdown hook called

16/04/26 11:35:58 INFO util.ShutdownHookManager: Shutdown hook called

僅根據異常無法判斷具體錯誤資訊,需要跟蹤其堆疊資訊。根據堆疊資訊,可以知道是AppClient(代表應用程式客戶端)中的ClientEndpoint(RPC通訊終端)嘗試註冊到Master時被拒絕 ——此時,檢查提交應用程式時使用的Master URL是否正確即可。

經測試驗證,當使用錯誤的Master URL時,會丟擲以上異常資訊。

3.3.2       自動上傳jar包方式

當部署模式為CLIENT時,應用程式(對應Driver)會將childMainClass設定為傳入的mainClass,然後啟動JVM程序,對應程式碼如下所示:

if (deployMode == CLIENT) {

  childMainClass = args.mainClass

  if (isUserJar(args.primaryResource)) {

    childClasspath += args.primaryResource

  }

  if (args.jars != null) { childClasspath ++= args.jars.split(",") }

  if (args.childArgs != null) { childArgs ++= args.childArgs }

}

在client模式,直接啟動應用程式的主類,同時,將主類的jar包和新增的jars包(如果在引數中設定的話)都新增到執行時的classpath中。即當前的driver的classpath會自動包含--jars 設定的jar包。

同時,driver通過啟動的http 服務上傳該jar包,executor在執行時下載該jar包,同時放置到executor程序的classpath路徑。

因此,--jars相當於:

1.      通過"spark.driver.extraClassPath"配置driver 端。

2.      通過"spark.executor.extraClassPath"配置executor端,同時將指定的jar包上傳到http 服務,並下載到executor端的該配置路徑下。

3.4     master為叢集的MasterURL+部署模式為Cluster時

CLUSTER部署模式時,Driver在某個節點提交,但卻是在叢集排程分配的節點上執行。

此時,可以將Driver看成是特殊的Executor,同樣由分配的節點執行JVM程序,但對應程序的classpath配置資訊(補充說明下,看配置屬性的extra名,應該可以知道是附加的,或新增的classpath內容,而不是全部)由各自對應的配置屬性進行設定。

CLUSTER部署模式在Drive分配到節點,並在節點上啟動,相關屬性配置及其作用等和CLIENT部署模式基本一致。這裡僅基於配置屬性方式針對Driver進行解析。

3.4.1       應用程式提交方式

下面給出兩種形式的提交命令:

1.      基於REST服務提交方式

$SPARK_HOME/bin/spark-submit  --master spark://nodemaster:6066 \

  --deploy-mode cluster \

  --driver-memory 2g \

  --driver-cores 1 \

  --total-executor-cores 2 \

  --executor-memory 4g \

  --conf "spark.ui.port"=4081 \

  --conf "spark.executor.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" \

  --conf "spark.driver.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar"   \

         --class com.mb.TestJarwithOracle \

         hdfs://nodemaster:8020/tmp/sptest/Spark15.jar

該方式採用REST服務作為masterurl提交應用程式。對應的值參考8080(預設)監控介面,如下所示:

1.5.2Spark Master at spark://nodemaster:7078

·        URL:spark://nodemaster:7078

·        RESTURL:spark://nodemaster:6066(cluster mode)

·        AliveWorkers:7

·        Coresin use:84 Total, 0 Used

·        Memoryin use:420.0 GB Total, 0.0 B Used

·        Applications:0 Running, 106 Completed

·        Drivers:0 Running, 11 Completed

·        Status:ALIVE

日誌:

Running Spark using the REST application submission protocol.

16/04/26 13:15:02 INFO rest.RestSubmissionClient: Submitting a request to launch an application in spark://nodemaster:7078.

16/04/26 13:15:03 WARN rest.RestSubmissionClient: Unable to connect to server spark://nodemaster:7078.

Warning: Master endpoint spark://nodemaster:7078 was not a REST server. Falling back to legacy submission gateway instead.

16/04/26 13:15:04 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

當REST 服務方式提交嘗試失敗後,會退回到傳統方式進行提交。

2.      傳統提交方式

$SPARK_HOME/bin/spark-submit  --master spark://nodemaster:7078 \

  --deploy-mode cluster \

  --driver-memory 2g \

  --driver-cores 1 \

  --total-executor-cores 2 \

  --executor-memory 4g \

  --conf "spark.ui.port"=4081 \

  --conf "spark.executor.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" \

  --conf "spark.driver.extraClassPath"="hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar"   \

         --class com.mb.TestJarwithOracle \

         hdfs://nodemaster:8020/tmp/sptest/Spark15.jar

日誌:

Running Spark using the REST application submission protocol.

16/04/26 13:20:58 INFO rest.RestSubmissionClient: Submitting a request to launch an application in spark://nodemaster:6066.

16/04/26 13:20:58 INFO rest.RestSubmissionClient: Submission successfully created as driver-20160426132058-0010. Polling submission state...

16/04/26 13:20:58 INFO rest.RestSubmissionClient: Submitting a request for the status of submission driver-20160426132058-0010 in spark://nodemaster:6066.

16/04/26 13:20:58 INFO rest.RestSubmissionClient: State of driver driver-20160426132058-0010 is now RUNNING.

16/04/26 13:20:58 INFO rest.RestSubmissionClient: Driver is running on worker worker-20160418110627-192.168.149.95-45661 at 192.168.149.95:45661.

16/04/26 13:20:58 INFO rest.RestSubmissionClient: Server responded with CreateSubmissionResponse:

{

  "action" : "CreateSubmissionResponse",

  "message" : "Driver successfully submitted as driver-20160426132058-0010",

  "serverSparkVersion" : "1.5.2",

  "submissionId" : "driver-20160426132058-0010",

  "success" : true

}

3.4.2       提交應用之後Driver的分析

兩種方式都可以成功提交應用程式,對應在介面會分別增加一個Driver,同時Driver啟動後,會和之前Client部署模式的流程一樣,提交一個Application(這裡的概念對應Executor),對應介面(8080預設埠介面-最下面)有:

兩個driver提交的應用如下(8080預設埠介面):

對應下面的Application,配置及其影響和前面是一樣的,只是當前的Driver在分配的節點上執行(所有相關路徑等概念都改為基於該執行節點)。因此下面僅分析Driver的相關內容。

資訊獲取相關操作:

1.       點選Driver行所在的Worker,跳轉到該Worker監控頁面,到最下面,查詢與Driver的SubmissionID相同的Driver資訊,介面如下所示:

2.       點選對應的stderr日誌資訊,可以看到Driver的啟動命令及其執行日誌

3.       檢視Driver的啟動命令

a)        對應傳統提交方式的日誌如下所示:

Launch Command: "/usr/java/jdk1.7.0_71/bin/java" "-cp" "hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar:$SPARK_HOME/sbin/../conf/:$SPARK_HOME/lib/spark-assembly-1.5.2-hadoop2.6.0.jar:$SPARK_HOME/lib/datanucleus-api-jdo-3.2.6.jar:$SPARK_HOME/lib/datanucleus-core-3.2.10.jar:$SPARK_HOME/lib/datanucleus-rdbms-3.2.9.jar:/etc/hadoop/conf/" "-Xms2048M" "-Xmx2048M" "-Dspark.deploy.defaultCores=4" "-Dspark.eventLog.enabled=true" "-Dakka.loglevel=WARNING" "-Dspark.history.fs.cleaner.maxAge=7d" "-Dspark.submit.deployMode=cluster" "-Dspark.executor.memory=4g" "-Dspark.executor.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" "-Dspark.executor.extraJavaOptions=-XX:+PrintGCDetails" "-Dspark.jars=hdfs://nodemaster:8020/tmp/sptest/Spark15.jar" "-Dspark.history.fs.cleaner.enabled=true" "-Dspark.master=spark://nodemaster:7078" "-Dspark.driver.supervise=false" "-Dspark.driver.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" "-Dspark.app.name=com.mb.TestJarwithOracle" "-Dspark.history.fs.logDirectory=hdfs://nodemaster:8020/user/hdfs/sparklogs" "-Dspark.driver.memory=2g" "-Dspark.cores.max=2" "-Dspark.rpc.askTimeout=10" "-Dspark.eventLog.dir=hdfs://nodemaster:8020/user/hdfs/sparklogs" "-Dspark.ui.port=4081" "-Dspark.history.fs.cleaner.interval=1d" "-Dspark.driver.cores=1" "-XX:MaxPermSize=256m" "org.apache.spark.deploy.worker.DriverWrapper" "akka.tcp://[email protected]:45661/user/Worker" "$SPARK_HOME/work/driver-20160426131505-0009/Spark15.jar" "com.mb.TestJarwithOracle"

b)       對應REST提交方式的日誌如下所示:

Launch Command: "/usr/java/jdk1.7.0_71/bin/java" "-cp" :$SPARK_HOME/sbin/../conf/:$SPARK_HOME/lib/spark-assembly-1.5.2-hadoop2.6.0.jar:$SPARK_HOME/lib/datanucleus-api-jdo-3.2.6.jar:$SPARK_HOME/lib/datanucleus-core-3.2.10.jar:$SPARK_HOME/lib/datanucleus-rdbms-3.2.9.jar" "-Xms2048M" "-Xmx2048M" "-Dspark.deploy.defaultCores=4" "-Dspark.eventLog.enabled=true" "-Dspark.history.fs.cleaner.maxAge=7d" "-Dspark.submit.deployMode=cluster" "-Dspark.executor.memory=4g" "-Dspark.executor.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" "-Dspark.executor.extraJavaOptions=-XX:+PrintGCDetails" "-Dspark.jars=hdfs://nodemaster:8020/tmp/sptest/Spark15.jar" "-Dspark.history.fs.cleaner.enabled=true" "-Dspark.master=spark://nodemaster:7078" "-Dspark.driver.supervise=false" "-Dspark.driver.extraClassPath=hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar" "-Dspark.app.name=com.mb.TestJarwithOracle" "-Dspark.history.fs.logDirectory=hdfs://nodemaster:8020/user/hdfs/sparklogs" "-Dspark.driver.memory=2g" "-Dspark.cores.max=2" "-Dspark.eventLog.dir=hdfs://nodemaster:8020/user/hdfs/sparklogs" "-Dspark.ui.port=4081" "-Dspark.history.fs.cleaner.interval=1d" "-Dspark.driver.cores=1" "-XX:MaxPermSize=256m" "org.apache.spark.deploy.worker.DriverWrapper" "akka.tcp://[email protected]:45661/user/Worker" "$SPARK_HOME/work/driver-20160426132058-0010/Spark15.jar" "com.mb.TestJarwithOracle"

(暫時不考慮兩者的差異,僅關注Driver程序相關的內容。)

下面對其中比較重要的幾個部分進行分析:

1.       其中,啟動的JVM程序主類為"org.apache.spark.deploy.worker.DriverWrapper",在"-cp"後加入了hdfs://nodemaster:8020/tmp/sptest/ojdbc14.jar,這裡對應的是提交命令中的--conf"spark.driver.extraClassPath"配置屬性(可通過修改設定的路徑進行驗證)。

在該啟動命令(提交命令的配置引數)中,ojdbc14.jar路徑為hdfs,因此無法識別,在driver部分的邏輯程式碼執行時會丟擲異常。對應日誌在該LaunchCommand:命令後面。因此"spark.driver.extraClassPath"配置屬性中設定的路徑應該對應當前節點(由於是排程分配的,對應就意味著應該是在叢集中各個節點都進行部署)的路徑,並且路徑下有所需jar包。

2.       對應的spark.driver.extraClassPath

相關推薦

基於Spark SQL Oracle簡單案例分析常見問題

1      概述 本文主要內容包含Spark SQL讀寫Oracle表資料的簡單案例,並針對案例中比較常見的幾個問題給出解決方法。 最後從常見的java.lang.ClassNotFoundException(無法找到驅動類)的異常問題出發,分析相關的幾種解決方

Spark SQL方法

data 類型 編程 記錄 sim 效率 top 行數 gty 一、DataFrame:有列名的RDD 首先,我們知道SparkSQL的目的是用sql語句去操作RDD,和Hive類似。SparkSQL的核心結構是DataFrame,如果我們知道RDD裏面的字段,也知道裏面的

ReentrantReadWriteLock簡單原理案例證明

## ReentrantReadWriteLock存在原因? --- 我們知道List的實現類ArrayList,LinkedList都是非執行緒安全的,Vector類通過用synchronized修飾方法保證了List的多執行緒非安全問題,但是有個缺點:**讀寫同步,效率低下**。於是就出現了CopyO

基於maxscale的分離部署筆記

基於 maxscale 讀寫分離 使用maxscale搭建的讀寫分離架構,後期還可以再結合MHA做master的故障轉移,這樣業務層面上不需要做任何的改動即可。基於connect方式的不要使用。從庫延遲他還會繼續分發請求過去,暫時不適合生產使用。 實驗演示:目前的主從結構:node93

Pythonoracle數據庫

strong dataframe rom cut 安裝 sim 用戶名 環境 操作 最近項目中需要用到Python調用oracle實現讀寫操作,踩過很多坑,歷盡艱辛終於實現了。性能怎樣先不說,有方法後面再調優嘛。現在把代碼和註意點記錄一下。 1. 所需Python工具庫

HDFS文件流程簡單圖解

http pla ges jpg eight 簡單 系統 mage pan 在活動反思文件系統中 HDFS文件讀寫流程簡單圖解

Python第三周之文件的以及簡單的爬蟲介紹

以及 under url error: except __name__ quest for div 文件的讀寫   讀 import time def main(): """ 文件的讀寫,註意open的用法以及,文件地址的輸入。 :retur

MySQL分離-簡單思考

本文圖片資源均來自網際網路,沒有乾貨,只是提供一種簡單的思路。 基礎原理 兩臺MySQL機器一個主,一個從實現資料實時同步比較簡單,程式碼層面無需任何修改,新增一臺機器簡單配置配置即可,但是MySQL資料庫實現讀寫分離,就有那麼點麻煩了。 如上面這張圖。 1 三臺Slave機器通過日誌記錄的方式,實時同

Linux 使用Mycat實現分離(基於Mysql的分離)

各位同學大家好,今天給大家分享一下用Mycat進行資料庫的讀寫分離,本篇文章是基於上一篇的mysql主從複製。Linux上實現Mysql的主從複製(為Mycat讀寫分離作準備) 在上一篇文章中,我們在兩個伺服器使用同版本的作業系統和mysql: 伺服器1:centos7.3,mysql5.6 伺服器

js cookie 簡單實現

  const getItem = key => { let ca = document.cookie.split('; '); for (let i = 0; i < ca.length; i++) { let item = ca[i].split('=');

50、mysql基於mysql-proxy分離實戰

一、主從配置 192.168.130.61 master 192.168.130.62 slave 192.168.130.63 proxy master配置 [mysqld] socket=/tmp/mysql.sock basedir=/usr/local/mysql datadi

基於spark SQL之上的檢索與排序對比效能測試

關於spark的效能,基於YDB的對比,做了一個測試,保留備用。 一、YDB與spark sql在排序上的效能對比測試 在排序上,YDB具有絕對優勢,無論是全表,還是基於任意條件組合過濾,基本秒殺spark任何格式。 測試結果(時間單位為秒) 詳細測試地

檔案工具簡單實現(一)之java的UI介面視覺化畫圖/製作

現在和大家介紹下,我們簡單的介面工具開發,使用者選擇檔案,讀出部分想要檔案內容,滑鼠在大輸入框點選某一行就會顯示在其他輸入框中,然後在其他輸入框修改後內容,點寫入按鈕即可修改並顯示到大輸入框中,且原檔案對應剛剛選的改行內容;大概的介面如下,網路現在原因不能上傳圖片:選擇檔案按

大資料實戰:基於Spark SQL統計分析函式求分組TopN

做大資料分析時,經常遇到求分組TopN的問題,如:求每一學科成績前5的學生;求今日頭條各個領域指數Top 30%的頭條號等等。Spark SQL提供了四個排名相關的統計分析函式: dense_rank() 返回分割槽內每一行的排名,排名是連續的。 rank() 返回分割槽

Spark WordCount hdfs檔案 (read file from hadoop hdfs and write output to hdfs)

create a scala project and a WordCount class as follow: package com.qiurc.test import org.apache.spark._ import SparkContext._ o

Java的RandomAccessFile隨機檔案簡單使用

import java.io.*; /** * Created by cuboo on 2016/10/10. */ public class io { public static vo

大資料-05-SparkHBase資料

準備工作一:建立一個HBase表 這裡依然是以student表為例進行演示。這裡假設你已經成功安裝了HBase資料庫,如果你還沒有安裝,可以參考大資料-04-Hbase入門,進行安裝,安裝好以後,不要建立資料庫和表,只要跟著本節後面的內容操作即可。 因為hbase依賴於hadoop,因此啟動和停止都是需要按

MySQL之——基於Amoeba實現分離

今天,我們繼續MySQL的話題,今天為大家帶來一篇基於Amoeba實現MySQL讀寫分離的方案文章,好我們直接進入今天的正題吧。 一、伺服器規劃 主機名 IP地址 節點 liuyazhuang152 192.168.0.152 amoeba liuyazhuang153

mysql基於amoeba的分離

    前文中有講到有關mysql的主從配置,如果單單就主從配置而言,僅能實現資料的及時備份與恢復,對於那些更新不頻繁,不需要時時備份資料的使用者來說意義不大,而且僅為了備份而再架設一臺資料庫伺服器有些浪費,因為使用免費的雲備份完全可以勝任,當然架設主從資料庫的意義當然不僅

sparklzo檔案(java)

1、編譯安裝hadoop-lzo 這個網上已經說的很詳細了,具體可以參考這個連結: 2、具體如何配置lzo,上面的文件已經有了,但是為了討論問題,這裡再複製一下: a、修改hadoop配置檔案core-site.xml  Xml程式碼  <prope