提交Apache Spark作业时在spark.jars中使用通配符的问题
解决Spark 2.3中HDFS通配符JAR包加载失败的问题
我之前碰到过一模一样的问题,这算是Spark 2.3里spark.jars参数的一个典型坑——它的通配符解析逻辑是在**提交作业的客户端(Driver所在节点)**完成的,而非在HDFS层面或者Executor端处理。
问题根源
当你设置spark.jars=hdfs:///path/to/my/libs/*.jar时:
- Driver启动阶段,客户端会尝试在本地文件系统解析这个通配符,但本地根本不存在
hdfs:///.../*.jar这条路径,所以通配符不会被展开,Driver只是把带*的原始路径直接传给了Executor。 - Executor拿到这个路径后,尝试读取时HDFS并不会自动解析路径里的*(因为Spark传递的是纯字符串路径,没有触发Hadoop FileSystem的路径解析逻辑),最终就会抛出
FileNotFoundException。
可行解决方案
方案1:提前展开HDFS通配符,生成完整JAR路径列表
你可以手动(或通过脚本、代码)先列出HDFS上的所有目标JAR,再把完整路径用逗号拼接后传给spark.jars。
方式A:Shell脚本预处理(spark-submit场景)
# 先获取HDFS上的JAR路径列表,用逗号拼接成字符串 JAR_LIST=$(hdfs dfs -ls hdfs:///path/to/my/libs/*.jar | awk '{print $8}' | tr '\n' ',') # 提交作业时传入处理后的列表 spark-submit \ --class com.your.package.YourJob \ --jars $JAR_LIST \ your-main-jar.jar
方式B:代码内动态生成(程序初始化SparkSession场景)
如果是在Scala/Java代码里构建SparkSession,可以借助Hadoop的FileSystem API来展开通配符:
import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.sql.SparkSession // 先初始化基础SparkConf,暂不设置spark.jars val conf = new org.apache.spark.SparkConf() .setAppName("YourJobName") val spark = SparkSession.builder() .config(conf) .getOrCreate() // 获取HDFS文件系统实例 val fs = FileSystem.get(spark.sparkContext.hadoopConfiguration) // 展开通配符路径,得到所有JAR的完整HDFS路径 val jarPaths = fs.globStatus(new Path("hdfs:///path/to/my/libs/*.jar")) .map(_.getPath.toString) .mkString(",") // 动态将JAR添加到Spark上下文 spark.sparkContext.addJar(jarPaths)
方案2:改用spark.driver.extraClassPath和spark.executor.extraClassPath
这两个参数的通配符是在**各自节点(Driver/Executor)**上由Hadoop类加载器解析的,HDFS路径的通配符可以正常生效:
spark-submit \ --class com.your.package.YourJob \ --conf spark.driver.extraClassPath=hdfs:///path/to/my/libs/*.jar \ --conf spark.executor.extraClassPath=hdfs:///path/to/my/libs/*.jar \ your-main-jar.jar
注意:这个方案要求所有Executor节点都能访问指定的HDFS路径(集群环境下通常都满足),另外extraClassPath的类加载优先级低于spark.jars,如果有依赖冲突需要留意顺序。
额外提醒
- 不要直接在
spark.jars里写HDFS通配符路径,除非你的客户端机器把HDFS路径挂载成了本地文件系统(比如NFS挂载,不推荐这种方式)。 - 如果JAR包数量较多,方案1的脚本方式会更高效,避免代码中额外的逻辑开销。
内容的提问来源于stack exchange,提问作者pay
相关产品推荐
相关产品推荐

