發(fā)的關(guān)鍵配置與排錯(cuò))
簡(jiǎn)介winutils-master.zip2.6.0-3.0.0是一份面向Windows平臺(tái)Hadoop跨系統(tǒng)調(diào)試的實(shí)用工具包主要幫助開(kāi)發(fā)者在本地Windows環(huán)境連接并測(cè)試Hadoop集群解決因缺少Windows專(zhuān)用本地庫(kù)而導(dǎo)致的啟動(dòng)失敗或通信異常。壓縮包共275個(gè)文件涵蓋exe、dll、lib、cmd、xml、asc等主要類(lèi)型其中exe與dll是運(yùn)行核心cmd與xml便于環(huán)境配置和參數(shù)調(diào)整lib和pdb支持二次編譯調(diào)試asc則用于文件校驗(yàn)整體體積僅7.13MB結(jié)構(gòu)清晰。該資源目前已有1008人學(xué)習(xí)下載適合正在從事大數(shù)據(jù)開(kāi)發(fā)或運(yùn)維、需要利用Windows進(jìn)行Hadoop相關(guān)調(diào)試的技術(shù)人員。使用時(shí)按Hadoop版本選擇對(duì)應(yīng)目錄將hadoop.dll復(fù)制到系統(tǒng)System32再將winutils.exe與hadoop.dll放入Hadoop的bin目錄重啟后即可正常調(diào)用相關(guān)命令有效減少跨平臺(tái)環(huán)境配置的困擾是提升本地開(kāi)發(fā)效率的常用輔助工具。這些文件組合起來(lái)可完整支撐Windows下的Hadoop客戶(hù)端運(yùn)行、文件操作與命令提交實(shí)用價(jià)值較高。 如果你在 Windows 上做過(guò)本地 Spark、Flink 或者 Hive 開(kāi)發(fā)大概率見(jiàn)過(guò)這么一條報(bào)錯(cuò)Could not locate executable null\bin\winutils.exe in the Hadoop binaries。我第一次看到這行輸出時(shí)還挺懵的Java 程序、IDEA、環(huán)境變量都正常憑什么一個(gè) exe 都找不到后來(lái)才搞明白這個(gè)叫 winutils 的東西是 Hadoop 體系在 Windows 平臺(tái)上繞不開(kāi)的一個(gè)“翻譯官”。缺了它Spark 連本地目錄的權(quán)限都管不了更不用說(shuō)連 HDFS。今天要聊的這個(gè)winutils-master.zip是網(wǎng)上非常常見(jiàn)的一個(gè)下載產(chǎn)物里面打包了從 Hadoop 2.6.0 到 3.0.0 各版本所需的 Windows 本地二進(jìn)制文件基本屬于“一份壓縮包解決全家問(wèn)題”。我會(huì)從它到底是什么、文件里每個(gè)東西是干嘛的、怎么配置才不踩坑、報(bào)錯(cuò)怎么排查這幾個(gè)角度把這個(gè)小工具徹底講透。1. 先搞清楚winutils 是什么為什么沒(méi)有它 Hadoop 生態(tài)跑不起來(lái)1.1 報(bào)錯(cuò)現(xiàn)場(chǎng)Windows 上跑 Spark 的第一道坎先還原一下經(jīng)典場(chǎng)景。你剛在 Windows 上搭好 Spark 開(kāi)發(fā)環(huán)境雙擊運(yùn)行一個(gè)讀本地 JSON 的入門(mén) demo控制臺(tái)刷出一片日志其中大概率混著這樣的片段WARN Shell: Did not find winutils.exe: {} java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.這個(gè)null\bin\winutils.exe是重點(diǎn)。它說(shuō)明程序的HADOOP_HOME是空的于是 Hadoop 的Shell類(lèi)拼路徑時(shí)拼出來(lái)一個(gè)null\bin\winutils.exe。有些 demo 加了容錯(cuò)繼續(xù)跑但后面一旦涉及文件權(quán)限校驗(yàn)、NativeIO 調(diào)用就會(huì)冒出各種奇怪異常。很多人第一次見(jiàn)到這個(gè)錯(cuò)就以為是 Spark 裝壞了其實(shí)跟 Spark 本身沒(méi)關(guān)系缺的是 Windows 平臺(tái)的 Hadoop 本地支撐文件。1.2 原理拆解winutils.exe 和 hadoop.dll 分別干了什么Hadoop 最初是為 Linux/Unix 設(shè)計(jì)的。在 Unix 上它要執(zhí)行chmod、chown這類(lèi)系統(tǒng)命令來(lái)管理文件權(quán)限也會(huì)調(diào)用本地庫(kù)加速底層的 IO 和壓縮。Windows 沒(méi)有/bin/chmod這種東西文件權(quán)限模型也完全不一樣。winutils.exe 就是把這些 Unix 語(yǔ)義命令在 Windows 上重新實(shí)現(xiàn)了一遍相當(dāng)于一個(gè)“翻譯官”Hadoop 說(shuō)我要chmod 777winutils 就調(diào)用 Windows 的權(quán)限接口去做等價(jià)操作。hadoop.dll 則更底層一些它是給 JVM 動(dòng)態(tài)加載的本地庫(kù)。Java 程序通過(guò) JNI 調(diào)用它去完成 NativeIO、壓縮 codec、CRC 校驗(yàn)這類(lèi)高性能操作。如果加載不了你會(huì)在日志里看到一條非常典型的老熟人警告WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable這條警告的意思就是“本地庫(kù)沒(méi)加載上接下來(lái)我用純 Java 實(shí)現(xiàn)頂著”。程序未必馬上崩但性能和底層能力會(huì)打折扣。所以這兩個(gè)文件一個(gè)管“命令翻譯”一個(gè)管“底層工具”缺一不可。1.3 版本對(duì)應(yīng)關(guān)系2.6.0-3.0.0 覆蓋了誰(shuí)winutils-master.zip 2.6.0-3.0.0的含義是這個(gè)壓縮包里包含從 Hadoop 2.6.0 到 3.0.0 之間主要小版本的獨(dú)立目錄。你選哪個(gè)版本不取決于你電腦裝了什么而取決于你的 Spark、Flink、Hive 工程里捆綁的 hadoop-client 版本。常見(jiàn)目錄典型使用場(chǎng)景hadoop-2.6.0比較老的項(xiàng)目CDH 5.x 那撥hadoop-2.7.xSpark 2.x 早期、Hive 2.xhadoop-2.8.xSpark 2.3 / 2.4 本地開(kāi)發(fā)非常常見(jiàn)hadoop-2.9.x部分 Flink 1.x 的 hadoop 依賴(lài)hadoop-3.0.0Spark 3.0 早期工程怎么確認(rèn)自己工程里的 hadoop 版本在項(xiàng)目依賴(lài)樹(shù)里搜hadoop-client或hadoop-common看它的 version 字段。Maven 工程可以直接在 IDEA 的 Maven 窗口里搜Gradle 工程可以看依賴(lài)報(bào)告關(guān)鍵詞就一個(gè)hadoop.version。2. 拿到 winutils-master.zip 之后文件結(jié)構(gòu)全拆解2.1 文件名里的 master 是什么意思zip 從哪來(lái)看到master這個(gè)后綴基本可以斷定這是從 GitHub 倉(cāng)庫(kù)主分支下載的壓縮包。在 GitHub 上任意一個(gè)倉(cāng)庫(kù)頁(yè)面點(diǎn) Code - Download ZIP下載下來(lái)的文件名就是倉(cāng)庫(kù)名-master.zip。這個(gè) zip 通常對(duì)應(yīng)網(wǎng)上流傳很廣的 winutils 倉(cāng)庫(kù)社區(qū)作者把 Hadoop 各版本的 Windows 二進(jìn)制文件按目錄整理好方便大家直接下載。它不是 Apache 官方發(fā)布的安裝包而是社區(qū)構(gòu)建產(chǎn)物。官方更希望你自行編譯但為了本地開(kāi)發(fā)效率絕大多數(shù)人選擇直接用現(xiàn)成編譯結(jié)果這完全可以理解。2.2 解壓后的目錄結(jié)構(gòu)長(zhǎng)什么樣解壓之后大致是這樣winutils-master/ ├── hadoop-2.6.0/ │ └── bin/ │ ├── hadoop.dll │ ├── hadoop.exp │ ├── hadoop.lib │ ├── hadoop.pdb │ ├── libwinutils.lib │ └── winutils.exe ├── hadoop-2.6.4/ ├── hadoop-2.7.1/ ├── hadoop-2.7.7/ ├── hadoop-2.8.3/ ├── hadoop-2.9.2/ ├── hadoop-3.0.0/ └── README.md具體版本目錄會(huì)因倉(cāng)庫(kù)更新快慢略有差別但規(guī)律是一致的每個(gè) hadoop 版本一個(gè)文件夾文件夾里再放一個(gè)bin目錄。實(shí)際配置時(shí)系統(tǒng)要找的路徑就是HADOOP_HOME\bin\winutils.exe所以看清楚這個(gè)嵌套關(guān)系很重要。2.3 六個(gè)文件逐個(gè)說(shuō)哪些必須、哪些只是編譯副產(chǎn)品bin目錄下通常有六個(gè)文件可能有人第一眼不知道哪些該留。整理成一張表文件類(lèi)型作用運(yùn)行是否需要winutils.exe可執(zhí)行文件提供 ls、chmod、chown 等 Unix 命令的 Windows 等價(jià)實(shí)現(xiàn)必須hadoop.dll動(dòng)態(tài)鏈接庫(kù)JVM 通過(guò) JNI 加載提供 NativeIO 和本地壓縮能力必須hadoop.exp導(dǎo)出文件C 鏈接階段才用不需要hadoop.lib導(dǎo)入庫(kù)C/C 二次開(kāi)發(fā)編譯時(shí)用不需要hadoop.pdb調(diào)試符號(hào)排查崩潰時(shí)用正常跑不需要不需要libwinutils.lib靜態(tài)庫(kù)給 C/C 開(kāi)發(fā)者鏈接用不需要實(shí)際只留winutils.exe和hadoop.dll就能跑但建議別急著刪保持目錄完整更省心。注意這兩個(gè)文件要放在同一個(gè)bin目錄下因?yàn)?winutils.exe 運(yùn)行本身也可能依賴(lài)同目錄的 hadoop.dll。3. 從零配置Windows 本地 Hadoop/Spark 環(huán)境實(shí)操記錄3.1 放置目錄與解壓細(xì)節(jié)路徑和文件鎖先決定把哪個(gè)版本用起來(lái)。比如工程依賴(lài)是 Hadoop 2.8.3我推薦的做法是從 zip 里把hadoop-2.8.3整個(gè)目錄復(fù)制出來(lái)放到一個(gè)干凈的地方例如D:\dev\hadoop-2.8.3。路徑要避開(kāi)中文、空格和特殊符號(hào)否則后面某些工具解析路徑時(shí)會(huì)莫名其妙地出問(wèn)題。還有兩個(gè)解壓時(shí)容易忽略的細(xì)節(jié)。第一Windows 自帶壓縮工具對(duì)深層路徑比較敏感如果解壓報(bào)路徑太長(zhǎng)建議用 7-Zip 這類(lèi)工具。第二從網(wǎng)上下載的 exe 和 dll 可能被系統(tǒng)“鎖定”右擊文件 - 屬性如果底部有“解除鎖定”復(fù)選框勾上再點(diǎn)確定。否則運(yùn)行時(shí)會(huì)提示“Windows 已保護(hù)你的電腦”或者直接加載失敗。3.2 環(huán)境變量HADOOP_HOME、PATH 和 IDE 三處都別漏核心配置就兩個(gè)變量HADOOP_HOME指向包含bin目錄的上級(jí)路徑PATH里加上%HADOOP_HOME%\bin。圖形界面在“系統(tǒng)屬性 - 環(huán)境變量”里加即可也可以用 PowerShell 一次性配好[Environment]::SetEnvironmentVariable(HADOOP_HOME, D:\dev\hadoop-2.8.3, User) [Environment]::SetEnvironmentVariable(Path, $env:Path ;D:\dev\hadoop-2.8.3\bin, User)建議用上面的方式而不是setx因?yàn)閟etx有 1024 字節(jié)能耗限制容易把原有 Path 截?cái)唷5@里有個(gè)很多人踩過(guò)的坑光配系統(tǒng)環(huán)境變量不夠IDEA 是 Java 進(jìn)程如果它是在你配置之前啟動(dòng)的不會(huì)讀到新環(huán)境變量。所以 IDE 里還有三處要處理運(yùn)行配置的 VM options 里加-Dhadoop.home.dirD:/dev/hadoop-2.8.3代碼啟動(dòng)時(shí)加System.setProperty(hadoop.home.dir, D:/dev/hadoop-2.8.3);或者干脆在 IDEA 的運(yùn)行配置 Environment variables 里加一對(duì)HADOOP_HOMED:/dev/hadoop-2.8.3PySpark 場(chǎng)景也類(lèi)似命令行先設(shè)好環(huán)境變量再啟動(dòng)腳本set HADOOP_HOMED:\dev\hadoop-2.8.3 set PATH%HADOOP_HOME%\bin;%PATH% python your_spark_job.py3.3 驗(yàn)證是否生效hadoop version 與 winutils 命令測(cè)試配置完一定要驗(yàn)證別直接上去跑 Spark。重新打開(kāi)一個(gè)全新的 cmd 窗口先執(zhí)行hadoop version如果配置正確會(huì)輸出類(lèi)似Hadoop 2.8.3 Subversion https://github.com/apache/hadoop -r ... Compiled by ... on ...如果提示“不是內(nèi)部或外部命令”說(shuō)明HADOOP_HOME或PATH沒(méi)配對(duì)檢查HADOOP_HOME下面是不是真的有一個(gè)bin\winutils.exe。再測(cè)試一下 winutils 本身winutils.exe ls D:\能列出 D 盤(pán)根目錄就基本沒(méi)問(wèn)題。后續(xù)跑 Hive 元數(shù)據(jù)時(shí)經(jīng)常還需要給臨時(shí)目錄授權(quán)這是另一個(gè)高頻操作winutils.exe chmod 777 C:\tmp\hiveSpark 默認(rèn)的 warehouse 路徑也可能因?yàn)闄?quán)限報(bào)錯(cuò)同理用winutils.exe chmod -R 777處理對(duì)應(yīng)目錄。3.4 別忘了 VC 運(yùn)行庫(kù)winutils.exe 和 hadoop.dll 是 Visual Studio 編譯出來(lái)的動(dòng)態(tài)產(chǎn)物依賴(lài)系統(tǒng)的 VC 運(yùn)行庫(kù)。如果缺失運(yùn)行時(shí)會(huì)報(bào)“找不到 msvcp120.dll”或“找不到 vcruntime140.dll”更常見(jiàn)的是彈出0xc000007b錯(cuò)誤。解決辦法很簡(jiǎn)單去微軟官網(wǎng)把 Visual C Redistributable 裝一遍。建議 x86 和 x64 都裝上雖然手頭 JDK 一般 64 位但有些老版本 winutils 依賴(lài)的運(yùn)行庫(kù)入口可能是 32 位的裝齊能少踩很多坑。4. 實(shí)際開(kāi)發(fā)中的典型坑與排查心得4.1 Could not locate executable null\bin\winutils.exe 的排查思路這句話(huà)在網(wǎng)上出現(xiàn)頻率極高排查順序基本固定確認(rèn)環(huán)境變量是否真的生效新開(kāi) cmd 執(zhí)行echo %HADOOP_HOME%如果為空說(shuō)明沒(méi)配上或者沒(méi)重開(kāi)終端。確認(rèn)HADOOP_HOME指向的目錄里存在bin\winutils.exe。很多人把HADOOP_HOME指到了版本文件夾的bin里面導(dǎo)致拼出來(lái)...\bin\bin\winutils.exe也是錯(cuò)。確認(rèn) IDE 是否完全重啟。IDEA 不會(huì)自動(dòng)感知系統(tǒng)環(huán)境變量的變化必須完全退出再啟動(dòng)。確認(rèn)是不是多個(gè)環(huán)境變量沖突。如果同時(shí)存在用戶(hù)級(jí)和系統(tǒng)級(jí)的HADOOP_HOME用戶(hù)級(jí)的會(huì)優(yōu)先覆蓋檢查一下值是否一致。4.2 版本不匹配手頭只有 3.0.0 但工程要 3.2 怎么辦很多人會(huì)遇到這種情況winutils-master.zip里最高只到 3.0.0但自己的 Spark 3.0 工程捆綁的 hadoop-client 是 3.2.0。這時(shí)候不用慌。本地開(kāi)發(fā)模式跑純本地任務(wù)時(shí)winutils 主要負(fù)責(zé)的是本地文件權(quán)限映射協(xié)議層面的東西都由 Hadoop Java 客戶(hù)端負(fù)責(zé)。所以用 3.0.0 去頂一頂大多數(shù)純本地 demo 都能跑通。但如果你的程序要連遠(yuǎn)程 Hadoop 3.2 集群做 HDFS 讀寫(xiě)版本差距就值得重視。最穩(wěn)妥的方案是去 winutils 倉(cāng)庫(kù)里看看有沒(méi)有對(duì)應(yīng) 3.2.0 或 3.3.x 的更新目錄如果倉(cāng)庫(kù)沒(méi)有就拉 Hadoop 源碼按官方文檔在 Windows 上編譯一次產(chǎn)出對(duì)應(yīng)版本的bin目錄。這事情看著復(fù)雜但按文檔走成功率很高。4.3 hadoop.dll 加載失敗與 0xc000007b比缺 winutils 更隱蔽的是 hadoop.dll 加載失敗。表面癥狀包括日志出現(xiàn)NativeCodeLoader警告、報(bào)java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$Windows.access0、或者直接彈0xc000007b。按優(yōu)先級(jí)檢查這幾個(gè)點(diǎn)VC 運(yùn)行庫(kù)裝了沒(méi)有x64、x86 都裝。JDK 是 64 位嗎如果 JDK 是 32 位而 hadoop.dll 是 64 位編譯的JVM 無(wú)論如何都加載不了。bin目錄在不在進(jìn)程的 PATH 里JVM 通過(guò)System.loadLibrary加載 dll本質(zhì)上還是依賴(lài) PATH 搜索。下載的 zip 是不是校驗(yàn)過(guò)GitHub 下載偶爾會(huì)損壞重新解壓一次可能就解決了。一個(gè)不太推薦但也有人真這么干的方案是把 hadoop.dll 復(fù)制到C:\Windows\System32。確實(shí)能強(qiáng)制加載但這是全局污染容易影響別的軟件別這么干。4.4 多版本切換與工程化配置技巧開(kāi)發(fā)環(huán)境里同時(shí)存在多個(gè) Hadoop 版本的工程是很常見(jiàn)的事。我現(xiàn)在的習(xí)慣是不再依賴(lài)全局HADOOP_HOME而是把 winutils 各版本統(tǒng)一放在D:\dev\hadoop-versions\下面每個(gè)項(xiàng)目的啟動(dòng)腳本或 IDE 運(yùn)行配置獨(dú)立指定。批處理啟動(dòng)腳本示例echo off set HADOOP_HOMED:\dev\hadoop-versions\hadoop-2.8.3 set PATH%HADOOP_HOME%\bin;%PATH% spark-submit.cmd --class com.example.Main your-job.jar %*IDEA 側(cè)則是每個(gè)運(yùn)行配置的 VM options 里寫(xiě)各自的-Dhadoop.home.dir。這樣互不干擾切換項(xiàng)目也不用反復(fù)改系統(tǒng)變量。4.5 常見(jiàn)問(wèn)題速查表癥狀可能原因處理方式Could not locate executable null\bin\winutils.exeHADOOP_HOME 為空或未生效配置 HADOOP_HOME重啟終端和 IDE日志出現(xiàn) Did not find winutils.exe路徑不對(duì)或 HADOOP_HOME 指向 bin 內(nèi)部檢查 HADOOP_HOME 是否存在 bin\winutils.exeNativeCodeLoader Unable to load native-hadoop libraryhadoop.dll 未加載裝 VC 運(yùn)行庫(kù)把 bin 加入 PATH應(yīng)用報(bào) 0xc000007b運(yùn)行庫(kù)缺失或架構(gòu)不匹配安裝 x64/x86 VC 運(yùn)行庫(kù)本地臨時(shí)目錄 permission denied權(quán)限模型不適應(yīng) Windowswinutils.exe chmod -R 777 目標(biāo)目錄殺軟攔截 exe/dllWindows 標(biāo)記外部下載文件文件屬性里解除鎖定最后說(shuō)點(diǎn)個(gè)人體會(huì)。我最早幫同事排查 winutils 問(wèn)題時(shí)對(duì)方堅(jiān)持說(shuō)環(huán)境變量配了但 IDEA 里就是報(bào)錯(cuò)。折騰了半天才發(fā)現(xiàn)他是給系統(tǒng)變量加了HADOOP_HOME但 IDEA 是通過(guò)開(kāi)始菜單快捷方式啟動(dòng)的根本沒(méi)繼承最新的系統(tǒng)環(huán)境變量。從那以后我就學(xué)乖了所有本地 Hadoop 相關(guān)配置一律優(yōu)先在工程內(nèi)通過(guò)System.setProperty或 IDE 運(yùn)行配置顯式指定而不是依賴(lài)全局環(huán)境變量。winutils 看起來(lái)是個(gè)不起眼的小文件但它確實(shí)是 Windows 上進(jìn)入 Hadoop 生態(tài)的第一道門(mén)檻。把版本、路徑、運(yùn)行庫(kù)之間的關(guān)系理清楚后面折騰 Spark、Flink、Hive 的本地開(kāi)發(fā)會(huì)順暢非常多。如果你用的 Hadoop 版本超過(guò)了 3.0.0建議直接去 winutils 倉(cāng)庫(kù)找對(duì)應(yīng)目錄找不到就自己動(dòng)手編一次整個(gè)過(guò)程本身也是對(duì) Hadoop 構(gòu)建體系的一次不錯(cuò)的學(xué)習(xí)。本文還有配套的精品資源點(diǎn)擊獲取