Hdfs 相對(duì)路徑與靜態(tài)代碼塊引起的問(wèn)題
本文轉(zhuǎn)載自微信公眾號(hào)「明哥的IT隨筆」,作者IT明哥。轉(zhuǎn)載本文請(qǐng)聯(lián)系明哥的IT隨筆公眾號(hào)。
前言
大家好,我是明哥。
HIVE 作為大數(shù)據(jù)生態(tài)的數(shù)倉(cāng)解決方案,因?yàn)闅v史的原因在很多行業(yè)很多公司都有著廣泛的應(yīng)用。對(duì)于比較復(fù)雜的業(yè)務(wù)邏輯,HIVE SQL 往往比較難以表達(dá),此時(shí)大家在開(kāi)發(fā)中往往會(huì)輔以 HIVE UDF。所以充分理解和掌握 HIVE UDF正確的表寫(xiě)和使用方式,是大數(shù)據(jù)從業(yè)人員必不可少的一項(xiàng)技能。
關(guān)于HIVE UDF 的使用,明哥在前段時(shí)間發(fā)過(guò)兩篇博文,分別是 “如何在 hive udf 中訪問(wèn)配置數(shù)據(jù)-方案匯總與對(duì)比” 和 “淺析 hive udaf 的正確編寫(xiě)方式- 論姿勢(shì)的重要性" ,兩篇博文描述的都是 HIVE UDF 在編寫(xiě)使用過(guò)程中容易犯的錯(cuò)誤。
但 UDF 編寫(xiě)使用過(guò)程中遇到的問(wèn)題往往很多遠(yuǎn)遠(yuǎn)不止以上兩個(gè),所以明哥決定編寫(xiě)一個(gè)系列 - “淺析 hive udf 的正確編寫(xiě)和使用方式 - 論姿勢(shì)的重要性“,以上兩篇博文可以算做這個(gè)系列中的系列一和系列二,本文是該系列的系列三。以下是正文。
時(shí)間緊張,急于知道結(jié)論的小伙伴,可以直接看最后一部分,問(wèn)題總結(jié)。
問(wèn)題現(xiàn)象與初步分析
產(chǎn)品部人員反饋,某 HIVE UDF 通過(guò) hive 的舊客戶端即 hive service --cli方式可以正常使用,但使用新客戶端 beeline 時(shí)卻會(huì)報(bào)錯(cuò),客戶端的報(bào)錯(cuò)信息沒(méi)有啥明確的有意義的信息,如下圖圖一和圖二所示:
beeline
hive
咨詢產(chǎn)品部開(kāi)發(fā)人員,該 UDF 的功能是返回給定業(yè)務(wù)日期的下一個(gè)業(yè)務(wù)日期,在背后會(huì)讀取 HDFS上的一個(gè)日期類配置文件;經(jīng)查看該 UDF 源碼,發(fā)現(xiàn)該日期類配置文件的路徑,使用的是相對(duì)路徑,如下圖所示:
code-relative-path
熟悉 HDFS 的小伙伴都知道 HDFS 有相對(duì)路徑的概念,即代碼中用相對(duì)路徑方式指定的文件, 在不同用戶執(zhí)行作業(yè)時(shí)會(huì)被解析為不同用戶的根目錄下的文件,比如相對(duì)路徑 dir1/fileA, 使用hive 用戶執(zhí)行時(shí)作業(yè)時(shí)會(huì)被解析為 /user/hive/dir1/fileA, 使用xyz用戶執(zhí)行作業(yè)時(shí)會(huì)被解析為 /user/xyz/dir1/fileA, 很多 hdfs 上的文件找不到的問(wèn)題都是因?yàn)樵撛颉R驗(yàn)橛械沫h(huán)境有的用戶能執(zhí)行成功而另外的環(huán)境另外的用戶執(zhí)行卻會(huì)失敗,我們往往戲謔是人品問(wèn)題,哈哈。
所以順藤摸瓜,看到這里有使用相對(duì)路徑指定文件,我們一個(gè)自然的思路是查看日志驗(yàn)證問(wèn)題。需要注意,這里要查看的是服務(wù)端的日志,即 beeline 連接的 hiveserver2 實(shí)例的日志,在 cdh 中一般是 /var/log/hive下。果不其然,看到了熟悉的報(bào)錯(cuò)信息:
hiveserver2-log
進(jìn)一步咨詢產(chǎn)品部人員,他們把該配置文件上傳到了 /user/root/ 目錄下,沒(méi)有上傳過(guò)其它目錄。這也解釋了,為什么 他們 hive service --cli 方式能夠成功,而 beeline方式如 beeline -u jdbc:hive2://xxx:10000/default -n userA -p passwd 方式卻會(huì)失?。阂?yàn)樗麄兪褂们罢邥r(shí)是固定在 root 登錄用戶的身份下提交的作業(yè)(這其實(shí)不太合規(guī)范,一般不建議用root身份運(yùn)行應(yīng)用程序),而使用后者時(shí)實(shí)際生效的用戶是 -n 參數(shù)指定的用戶而不是當(dāng)前登錄用戶!(沒(méi)有啟用用戶身份認(rèn)證或啟用ldap認(rèn)證時(shí),都是通過(guò) -n 參數(shù)指定用戶身份;啟用kerberos認(rèn)證時(shí),通過(guò)kerberos的 principal指定用戶身份)。這里還有個(gè)小細(xì)節(jié),如果沒(méi)有啟用身份認(rèn)證且 beeline后沒(méi)有使用 -n參數(shù)指定用戶,真正生效的用戶時(shí)anonyous匿名用戶,對(duì)應(yīng)的 home directory 是 /user/anonymous.
分析到這里,我們覺(jué)得只需要上傳該配置文件到對(duì)應(yīng)用戶的 home directory下,即可解決該問(wèn)題了。但實(shí)際驗(yàn)證發(fā)現(xiàn),啪啪打臉,同樣的問(wèn)題仍然存在!
問(wèn)題進(jìn)一步分析與解決
再次仔細(xì)查看相關(guān)代碼,發(fā)現(xiàn)了問(wèn)題所在:該 UDF 讀取配置文件的內(nèi)容,使用的是類的靜態(tài)代碼塊(不是靜態(tài)方法)。為什么使用靜態(tài)代碼塊會(huì)引起問(wèn)題?要回答這個(gè)問(wèn)題,需要比較扎實(shí)的 JAVA 功底, 和對(duì) UDF 執(zhí)行機(jī)制的深刻理解。小伙伴們可以下想下。
code-static
不賣(mài)關(guān)子,直接說(shuō)原因:我們知道 SQL 和 UDF 的解析編譯優(yōu)化和生成 mr/tez/spark 任務(wù)是在 hiveserver2 中進(jìn)行的,但并不是所有的 sql 和 udf 都會(huì)生成 mr/tez/spark 任務(wù),比如這里該 UDF就不會(huì)生成 mr/tez/spark 任務(wù),也不需要向 yarn 申請(qǐng)資源獲得 container 容器,而是直接在 hiveserver2 中執(zhí)行的。所以前幾次 UDF的失敗調(diào)用時(shí),該 hiveserver2 這個(gè)jvm 已經(jīng)加載了對(duì)應(yīng)的類,此次再次調(diào)用該 UDF 時(shí)不需要重新加載該類,自然也不會(huì)重新執(zhí)行類的靜態(tài)代碼塊,所以沒(méi)有重新讀取配置文件的內(nèi)容,所以沒(méi)有更新對(duì)應(yīng)的配置變量,執(zhí)行也就失敗了。
重新啟動(dòng)該 hiveserver2 實(shí)例后,再次提交該 udf,會(huì)重新加載對(duì)應(yīng)的類,并執(zhí)行其中的靜態(tài)代碼塊讀取配置文件的內(nèi)容,讀取成功后會(huì)更新對(duì)應(yīng)的配置變量,最后作業(yè)執(zhí)行成功。
問(wèn)題總結(jié)
- HDFS 有相對(duì)路徑的概念,即代碼中用相對(duì)路徑方式指定的文件, 在不同用戶執(zhí)行作業(yè)時(shí)會(huì)被解析為不同用戶的根目錄下的文件,比如相對(duì)路徑 dir1/fileA, 使用hive 用戶執(zhí)行時(shí)作業(yè)時(shí)會(huì)被解析為 /user/hive/dir1/fileA, 使用xyz用戶執(zhí)行作業(yè)時(shí)會(huì)被解析為 /user/xyz/dir1/fileA, 很多 hdfs 上的文件找不到的問(wèn)題都是因?yàn)樵撛?
- Hive SQL 和 UDF 的解析編譯和優(yōu)化是在 hiveserver2 中進(jìn)行的,解析編譯和優(yōu)化的結(jié)果一般是生成 mr/tez/spark 任務(wù),這些 mr/tez/spark 任務(wù)是在向 yarn 申請(qǐng)獲得的 container 容器對(duì)應(yīng)的 jvm 中執(zhí)行的;但并不是所有的 sql 和 udf 都會(huì)生成 mr/tez/spark 任務(wù),此時(shí)其真正的執(zhí)行就是直接在 hiveserver2 這個(gè)已經(jīng)存在的 jvm 中執(zhí)行的,該 hiveserver2 這個(gè) jvm 的生命周期跟 udf 的執(zhí)行無(wú)關(guān),如果涉及到配置環(huán)境變量,系統(tǒng)參數(shù),或加載類及執(zhí)行靜態(tài)代碼塊,要尤其小心,必要時(shí)需要重啟 hiveserver2;(udf 中需要謹(jǐn)慎只用靜態(tài)代碼塊,因?yàn)殪o態(tài)代碼塊只有在初次加載類的時(shí)候才會(huì)執(zhí)行)
- udf中讀取配置文件,有多種方式,常見(jiàn)的有:
配置文件使用絕對(duì)路徑指定,且在代碼中寫(xiě)死絕對(duì)路徑;
配置文件使用絕對(duì)路徑,但在代碼中通過(guò)讀取本地配置文件獲取hdfs上配置文件的最終絕對(duì)路徑;
配置文件使用相對(duì)路徑,在客戶現(xiàn)場(chǎng)部署時(shí)需要確定執(zhí)行作業(yè)的真正用戶身份,并上傳該配置文件到對(duì)應(yīng)用戶的跟目錄下的特定路徑;