一.引言
當一個分佈式任務中一個文件需要在全局使用時,最常見的方法就是使用廣播的形式,在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 傳入的多個路徑的文件: