一.引言
当一个分布式任务中一个文件需要在全局使用时,最常见的方法就是使用广播的形式,在dirver端读取随后分发到excutor,这里需要的时间是
1) dirver端读取文件时间
2) 广播分发到各excutor的时间
当文件大小逐渐增加到一个阈值,或者内存资源有瓶颈时,广播的时间就会变长,这时候就需要和下面这种方式进行比较,看哪种方式时间最快
1) --files 添加文件
2) 直接在excutor读取文件,各个task使用
二.Spark Submit 脚本 --files 添加
Tips:这里适用于yarn-cluster模式,如果是本地模式的话直接 scala.io.Source.fromFile(fileName) 即可。
1.提交日志信息查看
这里先铺垫一下,--files 传输的文件:
如果与当前提交集群处于同一集群,会提示当前数据源与目标文件存储系统相同,此时不会触发拷贝
INFO Client: Source and destination file systems are the same. Not copying
如果与当前提交集群处于不同集群,则会将源文件从源路径更新至当前文件存储系统
INFO Client: Uploading resource
2.脚本传入 --files
#!/bin/bash
spark-submit \
--class your.class \
--master yarn \
--executor-cores 2 \
--driver-memory 2g \
--deploy-mode cluster \
--executor-memory 2G \
--files "viewfs://c9/ ... /localMaterial" \
--num-executors 2 \
./your.jar --local false
资源配置这些根据自己需求配置,唯一需要注意的就是这里 --files 传入的路径地址的 HDFS 前缀要和上面日志提交时目标路径的 HDFS 路径保持一致,否则会报 FileNotExist 的错。
三. Excutor 端读取 --files 传入文件
通过 SparkFiles.get(fileName)获取 --filse 传入的文件,这里只需要文件名即可,不需要完整的文件路径,然后通过Scala.io.Source读即可,接下来partition内的逻辑就都可以使用该文件了。
val sc = new SparkContext(conf)
val inputRdd = sc.textFile(input)
val fileName = "localMaterial"
println("Driver端: " + SparkFiles.get(fileName))
inputRdd.foreachPartition(partition => {
val path = SparkFiles.get(fileName)
println("Excutor端: " + path)
val info = scala.io.Source.fromFile(path).getLines().toArray
partition.foreach(line => {
...
})
})
有一点需要注意的就是该文件在 Dirver 端get得到的路径与 Excutor 端get到的路径时不一致的,所以要区分是要在dirver初始化该文件还是在excutor端初始化:
Dirver端:
/data10/hadoop/local/.../application_1594195336447_37512905/spark-b06afbf7-8618-49f3-ab64-cd40ba3b3ee9/userFiles-d14c7c91-44ac-4f09-b27e-a5467445f080/localMaterial
Excutor端:
/data9/hadoop/local/.../application_1594195336447_37509831/container_e29_1594195336447_37509831_01_000007/./localMaterial
这是因为 --files 会先经过 dirver 端处理,dirver会调用 SparkFiles.addFile() 获取文件到dirver的临时目录,随后 excutor 读取的文件是从 dirver 这里 fetch 到 container 的工作目录,所有造成了两端的目录位置不一致,如果将 dirver 端的路径直接传给 excutor 端,同样会报 FileNotFound 的错误。
四. --files 传入多个文件
传入多个文件需要修改submit脚本和程序内部:
1.submit脚本
—files 内文件按 ’,’ 号隔开,例如:
--files "viewfs://c9/.../localMaterial,viewfs://c9/...localMaterial1" \
2.spark 内部读取
inputRdd.foreachPartition(partition => {
val path1 = SparkFiles.get("localMateria")
val path2 = SparkFiles.get("localMaterial")
...
partition.foreach(line => {
...
})
})
dirver端,excutor端都支持读取 --files 传入的多个路径的文件: