一.引言

当一个分布式任务中一个文件需要在全局使用时,最常见的方法就是使用广播的形式,在dirver端读取随后分发到excutor,这里需要的时间是

1) dirver端读取文件时间

2) 广播分发到各excutor的时间

当文件大小逐渐增加到一个阈值,或者内存资源有瓶颈时,广播的时间就会变长,这时候就需要和下面这种方式进行比较,看哪种方式时间最快

1) --files 添加文件

2) 直接在excutor读取文件,各个task使用

二.Spark Submit 脚本 --files 添加

Tips:这里适用于yarn-cluster模式,如果是本地模式的话直接 scala.io.Source.fromFile(fileName) 即可。

1.提交日志信息查看

from命令 spark spark-submit --files_Source

from命令 spark spark-submit --files_spark_02

这里先铺垫一下,--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 传入的多个路径的文件:

from命令 spark spark-submit --files_Source_03