spark-core_02: $SPARK_HOME/bin/spark-submit、spark-class脚本分析

本文解析了 Spark 的提交脚本 spark-submit 的工作流程,包括环境变量设置、Java 运行时配置、Assembly JAR 文件查找及最终的 Spark 应用启动过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继承上文“spark-core_01: $SPARK_HOME/bin/spark-shell脚本分析

一,$SPARK_HOME/bin/spark-submit

################################################

#从spark-shell调用之后,传进来--class org.apache.spark.repl.Main --name "Spark shell" --master spark://luyl152:7077

#先检测spark_home,然后去调用spark_home/bin/spark-class 会将org.apache.spark.deploy.SparkSubmit作为第一个参数,

#----- 会执行脚本spark-class org.apache.spark.deploy.SparkSubmit --class org.apache.spark.repl.Main --name"Spark shell" --master spark://luyl152:7077

#####################################

#!/usr/bin/envbash

if [-z "${SPARK_HOME}" ]; then

  export SPARK_HOME="$(cd "`dirname"$0"`"/..; pwd)"

fi

 

#disable randomized hash for string in Python 3.3+

exportPYTHONHASHSEED=0

#exec 执行完面的命令,exec 命令,是创建一个新的进程,只不过这个进程与前一个进程的ID是一样的。这样,原来的脚本剩余的部分代码就不能执行了,因为相当于换了一个进程。

exec"${SPARK_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit" $@"

二、$SPARK_HOME/bin/spark-class

###########################################################################

#--如果是spark-shell从spark-submit脚本传进来如下参数:

org.apache.spark.deploy.SparkSubmit --classorg.apache.spark.repl.Main --name "Spark shell" --masterspark://luyl152:7077

#如果自己的application则直接执行spark-submit 脚本传入自己的--class等参数信息就可以

###########################################################################

 

#!/usr/bin/envbash

#还是判断了一下SPARK_HOME环境变量是否存在

if [-z "${SPARK_HOME}" ]; then

  export SPARK_HOME="$(cd "`dirname"$0"`"/..; pwd)"

fi

#---执行shell,不加“. 和空格”也能执行:配置一些环境变量,它会将conf/spark-env.sh中的环境变量加载进来:

. "${SPARK_HOME}"/bin/load-spark-env.sh

 

# ---Find the java binary 如果有java_home环境变量会将java_home/bin/java给RUNNER

#if [ -n str ] 表示当串的长度大于0时为真

if [-n "${JAVA_HOME}" ]; then

  RUNNER="${JAVA_HOME}/bin/java"

else

  #---command -v 和which的功能一样

  if [ `command -v java` ]; then

    RUNNER="java"

  else

    echo "JAVA_HOME is not set">&2

    exit 1

  fi

fi


#--- Find assembly jar : 会先找spark_home/RELESE文本是否存在,如果存在将spark_home/lib目录给变量ASSEMBLY_DIR

SPARK_ASSEMBLY_JAR=

if [-f "${SPARK_HOME}/RELEASE" ]; then

  ASSEMBLY_DIR="${SPARK_HOME}/lib"

else

 ASSEMBLY_DIR="${SPARK_HOME}/assembly/target/scala-$SPARK_SCALA_VERSION"

fi

 

#---ls -1与ls -l的区别在于ls -1只会返回文件名,没有文件类型,大小,日期等信息。num_jars返回spark-assembly的jar有多少个

GREP_OPTIONS=

num_jars="$(ls-1 "$ASSEMBLY_DIR" | grep "^spark-assembly.*hadoop.*\.jar$"| wc -l)"

 

#---如果$num_jars为0,会报错并退出

if ["$num_jars" -eq "0" -a -z "$SPARK_ASSEMBLY_JAR"-a "$SPARK_PREPEND_CLASSES" != "1" ]; then

  echo "Failed to find Spark assembly in$ASSEMBLY_DIR." 1>&2

  echo "You need to build Spark beforerunning this program." 1>&2

  exit 1

fi

 

if [-d "$ASSEMBLY_DIR" ]; then

  #---ls 后面加 || true和不加效果一样,还是会返回这个串的:spark-assembly-1.6.0-hadoop2.6.0.jar

  ASSEMBLY_JARS="$(ls -1"$ASSEMBLY_DIR" | grep "^spark-assembly.*hadoop.*\.jar$" ||true)"

  #---这个$num_jars不能大于1,否则会退出

  if [ "$num_jars" -gt "1"]; then

    echo "Found multiple Spark assemblyjars in $ASSEMBLY_DIR:" 1>&2

    echo "$ASSEMBLY_JARS" 1>&2

    echo "Please remove all but onejar." 1>&2

    exit 1

  fi

fi

#---将spark_home/lib/spark-assembly-1.6.0-hadoop2.6.0.jar给变量SPARK_ASSEMBLY_JAR

SPARK_ASSEMBLY_JAR="${ASSEMBLY_DIR}/${ASSEMBLY_JARS}"

#---还是将spark_home/lib/spark-assembly-1.6.0-hadoop2.6.0.jar赋给别一个变量LAUNCH_CLASSPATH

LAUNCH_CLASSPATH="$SPARK_ASSEMBLY_JAR"

 

#Add the launcher build dir to the classpath if requested.

#---if [ -n str ] 表示当串的长度大于0时为真

if [-n "$SPARK_PREPEND_CLASSES" ]; then

 LAUNCH_CLASSPATH="${SPARK_HOME}/launcher/target/scala-$SPARK_SCALA_VERSION/classes:$LAUNCH_CLASSPATH"

fi

#--- 将spark_home/lib/spark-assembly-1.6.0-hadoop2.6.0.jar值给全局变量_SPARK_ASSEMBLY

export_SPARK_ASSEMBLY="$SPARK_ASSEMBLY_JAR"

 

#For tests

if[[ -n "$SPARK_TESTING" ]]; then

  unset YARN_CONF_DIR

  unset HADOOP_CONF_DIR

fi

 

#The launcher library will print arguments separated by a NULL character, toallow arguments with

#characters that would be otherwise interpreted by the shell. Read that in awhile loop, populating

# anarray that will be used to exec the final command.

#######################################################

#启动程序库将打印由空字符分隔的参数,以允许带有由shell解释的字符的参数。 在while循环中读取它,填充将用于执行最终命令的数组。


 

 #如果要调试输入如下$JAVA_OPTS环境变量即可

#export JAVA_OPTS="$JAVA_OPTS -Xdebug-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"

#如果想调试org.apache.spark.launcher.Main,需要将$JAVA_OPTS放在$RUNNER后面就可以了,

#如果想直接调用SparkSubmit.scala可以将$JAVA_OPTS放在 launcher.Main的后面

 

#-----spark-class先执行org.apache.spark.launcher.Main,由launcher.Main来解析脚本参数,然后由Main方法将打印的结果给while语句,再次执行SparkSubmit类中的main方法;

#小括号有,数组或执行的命令的意思

#java -cp spark_home/lib/spark-assembly-1.6.0-hadoop2.6.0.jarorg.apache.spark.launcher.Main org.apache.spark.deploy.SparkSubmit --class org.apache.spark.repl.Main--name "Spark shell" --master spark://luyl152:7077

 

#######################################################

 

CMD=()

whileIFS= read -d '' -r ARG; do

  echo "===========>"$ARG

  CMD+=("$ARG")

done< <("$RUNNER" -cp "$LAUNCH_CLASSPATH"org.apache.spark.launcher.Main "$@")

exec"${CMD[@]}"

 

#launcher.Main返回的数据存储到CMD数组中,可以通过打印查看$ARG里面的内容,实际上就是laucher.Main里面的main打印的参数

[root@luyl155 bin]# ./spark-shell --masterspark://luyl152:7077,luyl153:7077,luyl154:7077 --verbose /tool/jarDir/maven_scala-1.0-SNAPSHOT.jar

#ARG打印的内容如下,这样看来${CMD[@]}就是让 “java -cp 执行第三行包中类即SparkSubmit, 使用jvm参数(即第四,五,六,七行),然后执行SparkSubmit 后面的是SparkSubmit参数是 --class 等内容

 

===========>/usr/local/java/jdk1.8.0_91/bin/java

===========>-cp

===========>/data/spark-1.6.0-bin-hadoop2.6/conf/:/data/spark-1.6.0-bin-hadoop2.6/lib/spark-assembly-1.6.0-hadoop2.6.0.jar:

/data/spark-1.6.0-bin-hadoop2.6/lib/datanucleus-api-jdo-3.2.6.jar:/data/spark-1.6.0-bin-hadoop2.6/lib/datanucleus-rdbms-3.2.9.jar:

/data/spark-1.6.0-bin-hadoop2.6/lib/datanucleus-core-3.2.10.jar:/data/hadoop-2.6.5/etc/hadoop/

===========>-Xms1g

===========>-Xmx1g

===========>-Xdebug

===========>-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005

===========>org.apache.spark.deploy.SparkSubmit

===========>--class

===========>org.apache.spark.repl.Main

===========>--name

===========>Sparkshell

===========>--master

===========>spark://luyl152:7077,luyl153:7077,luyl154:7077

===========>--verbose

===========>/tool/jarDir/maven_scala-1.0-SNAPSHOT.jar

===========>spark://luyl152:7077,luyl153:7077,luyl154:7077

接下来分析一下org.apache.spark.launcher.Main源码


Caused by: java.lang.ClassNotFoundException: org.apache.spark.SparkConf at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[?:1.8.0_191] at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_191] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[?:1.8.0_191] at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_191] at org.apache.hadoop.hive.ql.exec.spark.HiveSparkClientFactory.generateSparkConf(HiveSparkClientFactory.java:263) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.spark.RemoteHiveSparkClient.<init>(RemoteHiveSparkClient.java:98) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.spark.HiveSparkClientFactory.createHiveSparkClient(HiveSparkClientFactory.java:76) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.spark.session.SparkSessionImpl.open(SparkSessionImpl.java:87) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.spark.session.SparkSessionManagerImpl.getSession(SparkSessionManagerImpl.java:115) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.spark.SparkUtilities.getSparkSession(SparkUtilities.java:136) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.spark.SparkTask.execute(SparkTask.java:115) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.Task.executeTask(Task.java:205) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.exec.TaskRunner.runSequential(TaskRunner.java:97) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.Driver.launchTask(Driver.java:2664) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.Driver.execute(Driver.java:2335) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.Driver.runInternal(Driver.java:2011) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.Driver.run(Driver.java:1709) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.Driver.run(Driver.java:1703) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hadoop.hive.ql.reexec.ReExecDriver.run(ReExecDriver.java:157) ~[hive-exec-3.1.2.jar:3.1.2] at org.apache.hive.service.cli.operation.SQLOperation.runQuery(SQLOperation.java:224) ~[hive-service-3.1.2.jar:3.1.2]
最新发布
07-27
<think>我们面对的错误是:`java.lang.ClassNotFoundException: org.apache.spark.SparkConf` 这个错误表明在运行Hive on Spark时,JVM无法在类路径中找到SparkConf类。这通常是因为Spark的相关库没有被正确加载到类路径中。 可能的原因和解决方案如下: 1. **Spark库未添加到Hive的类路径**: - 在Hive的配置中,需要确保Spark的相关JAR文件被添加到Hive的类路径中。可以通过设置`HIVE_AUX_JARS_PATH`环境变量或者修改`hive-site.xml`配置文件来添加。 - 例如,在`hive-site.xml`中添加: ```xml <property> <name>hive.aux.jars.path</name> <value>/path/to/spark/jars/*</value> </property> ``` 或者通过环境变量设置(在启动Hive之前): ```bash export HIVE_AUX_JARS_PATH=/path/to/spark/jars/* ``` 2. **Spark安装目录未正确设置**: - 确保`SPARK_HOME`环境变量设置正确,并且指向Spark的安装目录。Hive会使用`SPARK_HOME`来寻找Spark的库。 - 检查方法:`echo $SPARK_HOME` 3. **版本不兼容**: - Hive的版本和Spark的版本需要兼容。例如,Hive 3.1.2通常要求Spark 2.4.x版本。如果使用了不兼容的Spark版本(如Spark 3.x),则可能因为包结构变化(例如Spark 3.x中包名有变化)而导致类找不到。 - 解决方案:使用Hive官方文档中推荐的Spark版本。 4. **Spark JAR文件权限问题**: - 确保运行Hive的用户有权限读取Spark的JAR文件。 5. **多个Spark版本冲突**: - 如果系统中存在多个Spark版本,可能会发生冲突。确保Hive配置指向正确的Spark版本。 6. **Hive执行引擎配置**: - 确保Hive的执行引擎设置为Spark: ```sql SET hive.execution.engine=spark; ``` 或者永久配置在`hive-site.xml`中。 7. **检查Spark依赖库的路径**: - 确认`/path/to/spark/jars/`目录下确实包含`spark-core_2.11`(或相应版本)的JAR文件,其中应该有`org.apache.spark.SparkConf`类。 8. **使用Hive的`add jar`命令**: - 在Hive CLI中,可以尝试手动添加Spark的JAR文件: ```sql ADD JAR /path/to/spark/jars/spark-core_2.11-2.4.8.jar; ``` 注意:需要替换为实际路径和版本。 9. **检查Hive的日志**: - 查看详细的Hive日志,通常在`/tmp/<username>/hive.log`,以获取更多线索。 10. **使用Hive的Spark客户端模式**: - 在Hive 3.x中,支持两种Spark客户端:本地和远程。确保配置正确。例如,在`hive-site.xml`中设置: ```xml <property> <name>spark.master</name> <value>yarn</value> </property> ``` 根据以上分析,用户需要检查Spark库的路径配置、环境变量以及版本兼容性。 §§相关问题§§ 1. 如何正确配置Hive on Spark的执行环境? 2. Hive与Spark版本不兼容会导致哪些具体问题? 3. 在Hive中,如何手动添加JAR文件到当前会话? 4. 如何检查Hive的类路径中是否包含Spark的相关JAR? 5. 当遇到ClassNotFoundException时,有哪些通用的排查步骤?</think>### 错误分析:`java.lang.ClassNotFoundException: org.apache.spark.SparkConf` 这个错误表明 JVM 在运行时无法加载 Spark 的核心类 `SparkConf`,通常发生在 Hive 集成 Spark(Hive on Spark)的场景中。核心原因是 **Spark 库未正确加载到类路径**,具体原因和解决方案如下: --- ### 根本原因及解决方案: 1. **Spark JAR 文件缺失** - **问题**:Hive 依赖的 Spark 库(如 `spark-core_2.xx.jar`)未放入类路径 - **修复**: ```bash # 将 Spark jars 目录添加到 Hive 类路径 export HIVE_AUX_JARS_PATH=$SPARK_HOME/jars/* # 或在 hive-site.xml 中配置 <property> <name>hive.aux.jars.path</name> <value>/opt/module/spark/jars/*</value> </property> ``` 2. **SPARK_HOME 环境变量未设置** - **问题**:Hive 通过 `SPARK_HOME` 定位 Spark 库,未设置则无法加载 - **修复**: ```bash # 永久生效(写入 ~/.bashrc 或 /etc/profile) export SPARK_HOME=/opt/module/spark export PATH=$SPARK_HOME/bin:$PATH ``` 3. **版本不兼容** - **问题**:Hive 3.1.2 需搭配 Spark 2.4.x(非 Spark 3.x) - **验证**:检查兼容性矩阵 ```bash # 查看 Spark 版本 $SPARK_HOME/bin/spark-submit --version ``` - **方案**:降级 Spark 至 2.4.8 或升级 Hive 4. **类路径冲突** - **问题**:多个 Spark 版本 JAR 在类路径中冲突 - **排查**:检查 Hadoop 和 Hive 的 lib 目录 ```bash ls $HADOOP_HOME/share/hadoop/common/lib/*spark* ls $HIVE_HOME/lib/*spark* ``` - **解决**:移除冲突的旧版本 JAR --- ### 诊断步骤: 1. **验证类路径**: ```bash # 在 Hive 启动脚本中打印类路径 echo "CLASSPATH: $CLASSPATH" >> /tmp/hive_classpath.log ``` 2. **检查 SparkConf 是否存在**: ```bash unzip -l $SPARK_HOME/jars/spark-core_*.jar | grep SparkConf.class # 应输出:org/apache/spark/SparkConf.class ``` 3. **强制加载测试**: ```bash # 通过 Hive CLI 测试加载 hive -e "ADD JAR $SPARK_HOME/jars/spark-core_2.11-2.4.8.jar;" ``` --- ### 完整修复流程: ```bash # 1. 设置环境变量 echo "export SPARK_HOME=/opt/module/spark" >> /etc/profile source /etc/profile # 2. 修改 hive-site.xml cat <<EOF >> $HIVE_HOME/conf/hive-site.xml <property> <name>hive.aux.jars.path</name> <value>${SPARK_HOME}/jars/*</value> </property> EOF # 3. 重启 Hive 服务 hive --service metastore stop hive --service metastore start ``` > **注意**:若使用 CDH/HDP 等发行版,需通过管理界面同步配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值