在使用spark的時候,往往我們需要外部傳入文件,來配合程序做數據處理
那麼這就涉及到,如何傳入,如何獲取(本文討論的是spark on yarn)
講實話,我覺得這個問題挺煩的,我百度了好久(可能我姿勢不對?),各種博客,stackoverflow,community.cloudera.com都找過,我覺得回答方都停留在理論基礎,並沒有show me code,我實際測試的時候,好像又和他們說的不太一樣,哎,要是能有統一的入口,統一的出口就好了
1、client模式
client模式下,driver開啓在提交任務的機器上,所以他可以直接讀取到本地的文件,這就很簡單了
(1)、從shell中傳入文件的絕對路徑(關鍵在spark.filename這一行)
bin/spark-submit \
--master yarn \
--class spark.LoadFileTest \
--deploy-mode client \
--conf spark.file.absolutepath=/opt/data/sql.txt \
/opt/CDH/spark-2.2.0-bin-2.6.0-cdh5.12.1/spark.jar
其實隨便找個名字,不一定要用spark.file.absolutepath來作爲參數名稱,但是你的參數名稱必須是spark開頭的,纔會被sparkConf加載
(2)、在代碼中獲取到shell傳入的路徑,然後通過java的io流讀取數據
public static void main(String[] args) {
SparkSession sparkSession = buildSparkSession(false, "LoadFileTest");
String absolutePath = sparkSession.conf().get("spark.file.absolutepath");
System.out.println("===============文件絕對路徑=================");
System.out.println("absolutePath = "+ absolutePath);
String sql = readFileContent(absolutePath);
System.out.println("文件內容 = "+ sql);
}
private static String readFileContent(String absolutePath) {
BufferedReader reader = null;
StringBuffer stringBuffer = new StringBuffer();
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(absolutePath)));
String str;
//一次讀取一行
while ((str = reader.readLine()) != null) {
stringBuffer.append(str + "\n");
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return stringBuffer.toString();
}
(3)、獲得了之後想幹嘛就幹嘛吧,如果想讓executor處理的時候也用到這個數據,可以把這個數據做爲廣播變量廣播出去就好了
2、cluster模式
cluster模式下,driver會在任意有資源的節點啓動,那麼他就讀取不到當前提交任務的這臺機器的數據了,所以理論上,我們需要提供一個分佈式的文件系統,讓所有機器都可以通過這個系統獲取數據
(1)、在shell中使用--files的方式上傳文件,並且還要傳入文件名
bin/spark-submit \
--master yarn \
--class spark.LoadFileTest \
--deploy-mode client \
--conf spark.filename=sql.txt \
--files /opt/sql.txt \
/opt/CDH/spark-2.2.0-bin-2.6.0-cdh5.12.1/spark.jar
通過以上shell,你會發現日誌中有一條info,把本地的文件上傳到了hdfs上,這樣就可以通過hdfs訪問這個數據
(2)、代碼中獲取
可以通過spark的read直接讀數據,也可以自己寫io流讀取hdfs上的文件,這裏我選的第一種,比較方便
public static void main(String[] args) {
SparkSession sparkSession = buildSparkSession(false, "LoadFileTest");
String fileName= sparkSession.conf().get("spark.file.absolutepath");
System.out.println("===============文件名=================");
System.out.println("fileName= "+ fileName);
Dataset<String> confSql = spark
.read()
.textFile(System.getenv("SPARK_YARN_STAGING_DIR") + "/" + fileName);
confSql.show(false);
//這裏可以通過take,collect等方法,把Dataset的中的數據取出轉成String
//由於太簡單我就不寫了
}
有同學會問,誒,你這個SPARK_YARN_STAGING_DIR哪裏來的呀?
通過cluster提交的任務,我們無法在提交的機器直接看到日誌,所以我的hadoop開啓了jobhistory服務之後,我運行如下查看日誌的命令
bin/yarn logs -applicationId 這裏傳入具體的$application_id
3、備註
另我覺得煩的事情是這樣的,我在想,有沒有統一的方法,不管是client還是cluster都可以使用這個方法讀取數據
不知道是就沒有這樣的方法,還是我找不到
你看,理論上來說,client模式,我也通過--files把文件上傳到hdfs上,然後讀取hdfs上的數據就好了呀,但是關鍵在於client模式下,SPARK_YARN_STAGING_DIR居然爲null,這實在太難受了。。。。那我就不能方便的得到--files上傳的數據的目錄了,所以client模式我纔會用io直接讀取本地文件,不知道各位老哥們有沒有解決這個問題的方法呢?
所以如果不確定用戶提及shell中,會使用client還是cluster,那代碼中,只能先加一層判斷,判斷sparkSession.conf().get("spark.submit.deployMode")這個值是client還是cluster,然後再調用對應模式下的文件的讀取,想想就覺得好煩啊!
好了菜雞一隻,下次再見!
馬上端午節加上我的生日到了,提前祝自己生日快樂吧!