境用IntelliJ IDEA編譯CDH 5.14 Hadoop源碼指南)
在x86架構(gòu)的MacBook上拿IntelliJ IDEA去編譯Hadoop 2.6.0-cdh5.14.0這個(gè)組合聽起來就像是給十年前的項(xiàng)目做考古修復(fù)。但只要你的公司里還跑著CDH 5.14的集群或者你接手了一個(gè)基于CDH源碼做二次開發(fā)的模塊這事兒就繞不開。我為了在本地調(diào)試一個(gè)HDFS的定制功能花了整整一個(gè)周末把這條編譯鏈路徹底走通過程中踩掉的坑幾乎覆蓋了所有能在macOS上遇到的經(jīng)典問題。先說結(jié)論CDH 5.14的Hadoop雖然內(nèi)核是Apache Hadoop 2.6.0但它的源碼結(jié)構(gòu)、依賴坐標(biāo)、構(gòu)建腳本和原生庫編譯方式都做了不少改動直接拿Apache版本的教程來套十有八九會在中途報(bào)錯(cuò)。這篇內(nèi)容就是記錄我在x86 macOSIntel芯片10.14/10.15帶Xcode Command Line Tools上用IDEA編譯這套源碼的全過程順便把那些報(bào)錯(cuò)信息一條條對出來講清楚。1. CDH 5.14的Hadoop 2.6源碼為什么值得在macOS上費(fèi)力1.1 它和Apache Hadoop 2.6.0的差異CDH是Cloudera發(fā)行版的代號5.14.x對應(yīng)的是2017年前后的一批穩(wěn)定版本當(dāng)中的Hadoop主版本就是2.6.0-cdh5.14.0。很多人以為Cloudera只是換個(gè)殼實(shí)際不是。CDH在Apache Hadoop之上打了大量patch包括HDFS的Sentry集成、HA故障切換的細(xì)化、配額和審計(jì)日志的改動以及一堆bugfix。這些patch不是注釋級別的修改而是直接改變了源碼樹的結(jié)構(gòu)。你在IDEA里打開CDH倉庫會發(fā)現(xiàn)它比Apache版本多出不少子模塊比如hadoop-sentry、hadoop-fairscheduler-ext甚至在hadoop-hdfs模塊里也多了一些Cloudera自己加的類。如果你直接拿Apache Hadoop 2.6的源碼包來編譯跑出來的東西和公司CDH集群上跑的二進(jìn)制不一致二開出來的代碼很可能在線上行為完全不同。所以做CDH體系的二次開發(fā)編譯CDH自己的源碼是第一步不是可選步驟。1.2 哪些場景下需要本地編譯我遇到的需求很典型公司那套CDH 5.14集群上有個(gè)HDFS的NameNode內(nèi)存監(jiān)控邏輯是定制過的代碼在運(yùn)維團(tuán)隊(duì)手里但文檔基本等于沒有。我需要把整套源碼拉下來在本地跑一個(gè)偽分布式的NameNode和DataNode打斷點(diǎn)看路徑才能搞清楚它到底改了哪些行為。這種場景下本地編譯有幾個(gè)硬性要求編譯結(jié)果要和線上CDH版本一致不能用Apache原版代替要能在IDE里直接啟動HDFS進(jìn)程而不是打包完丟到服務(wù)器上黑盒運(yùn)行需要把native library編出來否則部分JNI調(diào)用會報(bào)warning甚至某些加密代碼路徑跑不起來如果你也是沖著這些目標(biāo)來的那這篇筆記應(yīng)該能幫你省掉不少時(shí)間。2. 環(huán)境準(zhǔn)備先對付JDK、Maven和protobuf三條攔路虎2.1 JDK只能選8原因在這里CDH 5.14的Hadoop 2.6時(shí)代官方推薦的是JDK 7和JDK 8但我建議直接裝JDK 8。原因有兩個(gè)第一JDK 7在Intel Mac上已經(jīng)很難找到合適的macOS版本且Oracle早就停止支持沒必要給自己找麻煩。第二JDK 8是這套源碼的實(shí)測安全區(qū)間編譯時(shí)不會碰到模塊化問題。如果你用JDK 9或更高版本會立刻在編譯期撞上javax.annotation和javax.xml.bind找不到的問題因?yàn)镴DK 9模塊化之后這些包不再默認(rèn)包含在classpath里了。Hadoop 2.6的代碼里不少類依賴它們沒有添加--add-modules java.xml.bind的舊版構(gòu)建邏輯基本過不去。我的建議是安裝一個(gè)干凈的JDK 8比如jdk1.8.0_291.jdk然后把JAVA_HOME明確指過去export JAVA_HOME/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home export PATH$JAVA_HOME/bin:$PATH注意x86 macOS的Intel Mac不需要額外處理Rosetta但如果你用的是Apple Silicon Mac還得給這整套工具鏈加一層x86轉(zhuǎn)譯那就更折騰了。所以標(biāo)題里強(qiáng)調(diào)x86架構(gòu)是有實(shí)際意義的Intel Mac至少不用處理arch匹配問題。2.2 Maven版本不要貪心3.3.9最穩(wěn)Hadoop 2.6的pom結(jié)構(gòu)是在Maven 3.2/3.3時(shí)代寫的。我一開始用的是Maven 3.8.6結(jié)果一堆老插件直接翻車比如maven-remote-resources-plugin報(bào)執(zhí)行失敗maven-antrun-plugin的腳本在解析時(shí)也出現(xiàn)了古怪的格式錯(cuò)亂。后來我換成了Maven 3.3.9整個(gè)編譯過程就順暢多了。如果是新裝的機(jī)器直接用Homebrew安裝指定版本brew install maven3.3裝完之后確認(rèn)版本mvn -version只要輸出里能看到Maven 3.3.9和正確的Java version: 1.8就可以進(jìn)入下一步。別小看這個(gè)版本對齊我在這個(gè)環(huán)節(jié)浪費(fèi)了一個(gè)下午最后發(fā)現(xiàn)就是Maven太新惹的禍。2.3 protobuf必須卡在2.5.0這是整個(gè)編譯過程中最不能妥協(xié)的一個(gè)依賴。Hadoop 2.6的RPC協(xié)議定義用的protobuf是2.5.0HDFS的NameNodeRpcServer和DataNode之間的通信協(xié)議由一系列.proto文件生成Java代碼。如果protoc編譯器版本不是2.5.0生成的代碼會在運(yùn)行時(shí)出現(xiàn)協(xié)議不匹配的異常。CDH源碼里的hadoop-common目錄下一堆.proto文件的語法是proto2寫法protobuf 2.5.0能正常解析。你要是裝個(gè)3.x的protoc雖然大部分proto2語法它也兼容但某些生成類的簽名和Hadoop源碼里寫死的接口不一致編譯期就會報(bào)錯(cuò)。我安裝protobuf 2.5.0的方式是下載官方源碼包在本地編譯安裝tar -zxvf protobuf-2.5.0.tar.gz cd protobuf-2.5.0 ./configure --prefix/usr/local/protobuf-2.5.0 make -j4 make install編譯完后把/usr/local/protobuf-2.5.0/bin放到PATH的最前面再用protoc --version確認(rèn)輸出為libprotoc 2.5.0。在比較新的macOS系統(tǒng)上protobuf 2.5.0的C代碼可能會因?yàn)榫幾g器太新而出一些兼容性報(bào)錯(cuò)我當(dāng)時(shí)的處理方式是直接用CC/usr/bin/clang、CXX/usr/bin/clang來configure注意不要讓它默認(rèn)找Homebrew里的更新版GCC老版本源碼對新編譯器反而更敏感。2.4 其他系統(tǒng)級依賴除了JDK、Maven、protobufnative部分編譯還需要cmake和一堆構(gòu)建工具。我在x86 macOS上預(yù)先用Homebrew裝好了這些brew install cmake autoconf automake libtool snappy brew install openssl zlib bzip2openssl尤其重要。如果你編譯時(shí)開啟了native庫的OpenSSL支持卻找不到頭文件會直接報(bào)openssl/evp.h不存在。Homebrew安裝的openssl是/usr/local/opt/opensslHadoop的configure腳本不一定會自動找到這個(gè)路徑后面如果有需要我會講怎么利用環(huán)境變量把這個(gè)路徑傳進(jìn)去。到這里環(huán)境準(zhǔn)備算是告一段落。我建議你先把每個(gè)依賴的版本都確認(rèn)一遍然后再啟動Maven構(gòu)建。磨刀不誤砍柴工在這個(gè)項(xiàng)目上尤其適用。3. Maven構(gòu)建我的關(guān)鍵參數(shù)選擇與執(zhí)行順序3.1 官方打包命令逐一拆解CDH源碼根目錄下的README會告訴你用Maven構(gòu)建但給的命令很籠統(tǒng)。實(shí)戰(zhàn)下來我用的命令是這樣的mvn clean install -DskipTests \ -Dmaven.javadoc.skiptrue \ -Dfindbugs.skiptrue \ -Dtarfalse \ -Pdist,native逐個(gè)參數(shù)看它做了什么-DskipTests跳過測試執(zhí)行但保留測試代碼的編譯。這一步很重要因?yàn)镠adoop的測試用例量很大很多測試需要啟動本地進(jìn)程在macOS上經(jīng)常因?yàn)槎丝谡加没蛳到y(tǒng)權(quán)限失敗。-Dmaven.javadoc.skiptrue跳過javadoc生成。這個(gè)純粹是為了省時(shí)間一個(gè)模塊的javadoc就要跑好幾分鐘。-Dfindbugs.skiptrue跳過FindBugs靜態(tài)檢查。CDH的老pom里findbugs插件版本比較舊在JDK 8的新字節(jié)碼格式下經(jīng)常誤報(bào)而且跑起來極慢。-Dtarfalse不生成tar.gz發(fā)行包。這個(gè)參數(shù)能讓構(gòu)建跳過不少打包后處理縮短時(shí)間。-Pdist,native激活兩個(gè)profile。dist會生成一個(gè)完整可運(yùn)行的Hadoop發(fā)行目錄native會觸發(fā)JNI和native代碼庫的編譯。如果你的目的只是把Java源碼編譯好然后導(dǎo)入IDEA其實(shí)可以暫時(shí)不加-Pnative后面單獨(dú)編native庫。我第一次就直接上了全量native結(jié)果一堆依賴問題全涌上來反而不利于定位。所以我的建議是分兩步走第一步先純Java編譯mvn install -DskipTests -Dmaven.javadoc.skiptrue -Dfindbugs.skiptrue -Dtarfalse這一步能通過說明Java層面的所有源碼、資源依賴都沒問題。第二步再考慮native。3.2 子模塊依賴順序問題CDH 5.14的源碼是一個(gè)超多模塊的Maven聚合工程模塊依賴呈現(xiàn)明顯的分層。你從根目錄執(zhí)行mvn installMaven會計(jì)算依賴拓樸并依次構(gòu)建但有些時(shí)候如果根pom的倉庫配置不全某個(gè)中間子模塊會拉不到依賴然后一直失敗。我在構(gòu)建過程中就遇到過hadoop-project-dist模塊報(bào)錯(cuò)問題不是代碼而是它依賴的hadoop-client和hadoop-minicluster中的某些CDH版jar在Maven中央倉庫根本沒有只有Cloudera的倉庫里有。如果你的Maven日志里出現(xiàn)Could not find artifact org.apache.hadoop:hadoop-hdfs:jar:2.6.0-cdh5.14.0之類的錯(cuò)誤多半就是倉庫列表不夠。解決方法是把Cloudera倉庫加進(jìn)settings.xml的profile里profile idcloudera/id repositories repository idcloudera-repos/id urlhttps://repository.cloudera.com/artifactory/cloudera-repos//url snapshots enabledfalse/enabled /snapshots /repository /repositories /profile然后在activeProfiles里激活它。這樣Maven在解析依賴時(shí)中央倉庫找不到的CDH專屬構(gòu)件就會自動去Cloudera倉庫拉取。3.3 首次構(gòu)建的耗時(shí)和內(nèi)存控制Hadoop這種規(guī)模的聚合工程首次構(gòu)建會下載幾百個(gè)jar加上Java源碼編譯整體耗時(shí)在30到60分鐘之間具體看機(jī)器性能。x86 Mac上我實(shí)測大概是40分鐘出頭。如果中途某個(gè)子模塊編譯失敗修改后重新執(zhí)行mvn install時(shí)由于前面的模塊已經(jīng)安裝了第二次的速度會快不少。另外要特別注意Maven JVM內(nèi)存。Hadoop編譯時(shí)會啟動多個(gè)插件進(jìn)程內(nèi)存不足會出現(xiàn)java.lang.OutOfMemoryError: PermGen space。雖然JDK 8已經(jīng)沒有PermGen但部分老插件還是會申請較大的堆內(nèi)存。我習(xí)慣了在編譯前設(shè)置export MAVEN_OPTS-Xmx4096m -XX:MaxPermSize512m這個(gè)設(shè)置對后面導(dǎo)入IDEA也有幫助IDEA里的Maven importer同樣會用到這類內(nèi)存參數(shù)。構(gòu)建成功以后你會看到一堆BUILD SUCCESS尤其是最后幾個(gè)核心子模塊的構(gòu)建結(jié)果這就說明命令行層面的編譯已經(jīng)打通了。接下來才輪到IDEA出場。4. 從命令行到IntelliJ IDEA導(dǎo)入與運(yùn)行配置4.1 導(dǎo)入前的倉庫預(yù)熱很多人喜歡把源碼直接拖進(jìn)IDEA讓IDEA自己去解析Maven然后卡在indexing和依賴下載上半天。我試過CDH這個(gè)工程模塊實(shí)在是多IDEA的首次導(dǎo)入會掃描所有pom并建立索引不預(yù)熱的話體驗(yàn)很差。更順滑的做法是先用命令行執(zhí)行一次完整的mvn install跳過測試即可把本地Maven倉庫填滿。這樣IDEA導(dǎo)入時(shí)所有依賴都能從本地倉庫直接讀取解析速度會快很多也基本不會出現(xiàn)Cannot resolve symbol的紅字。如果你遇到IDEA里的Maven窗口顯示某些dependency還是紅的點(diǎn)一下Reload All Maven Projects然后檢查IDEA使用的settings.xml路徑是否和命令行一致。這一步很關(guān)鍵IDEA默認(rèn)有自己的一套User settings路徑如果它和命令行用的不是同一個(gè)就會覺得依賴是亂的。4.2 配置Project SDK和Maven Runner在IDEA的File - Project Structure - Project里把Project SDK設(shè)為1.8Language Level也設(shè)為8。然后進(jìn)入Settings - Build, Execution, Deployment - Build Tools - MavenMaven home path指向Maven 3.3.9的安裝目錄User settings file指向你配置了Cloudera倉庫的settings.xmlLocal repository指向命令行使用的本地倉庫這些配置對齊以后Maven窗口里的子模塊列表會清晰很多。注意導(dǎo)入的時(shí)候IDEA可能會問你是否信任這個(gè)Maven project選擇信任否則某些插件執(zhí)行會被攔截。這里還有個(gè)容易忽略的地方IDEA內(nèi)置的編譯器和Maven編譯器是兩個(gè)獨(dú)立系統(tǒng)。如果你直接在IDEA的工具欄點(diǎn)Build它用的可能是IDEA自己的編譯器報(bào)錯(cuò)信息經(jīng)常和Maven構(gòu)建不一樣。所以我個(gè)人的習(xí)慣是先用Maven窗口執(zhí)行clean和install確認(rèn)源碼本身沒問題然后再用IDEA的Build來增量編譯做代碼跳轉(zhuǎn)和調(diào)試。4.3 調(diào)整IDEA的內(nèi)存和索引設(shè)置CDH 5.14工程包含的子模塊數(shù)量很多再加上自帶的三方依賴IDEA首次打開時(shí)索引任務(wù)會很重。x86 Mac上如果內(nèi)存只有8G建議在Help - Change Memory Settings里把IDEA的堆內(nèi)存調(diào)到至少2G否則卡到鍵盤冒煙。導(dǎo)入完成后你可以試著搜索一個(gè)核心類比如NameNode如果能正常跳轉(zhuǎn)到hadoop-hdfs模塊的源碼說明整個(gè)工程的索引已經(jīng)建立成功。如果跳轉(zhuǎn)失敗可能是這個(gè)模塊沒有被IDEA正確識別為源碼目錄右鍵對應(yīng)根目錄在Mark Directory as里選擇Sources Root。這些準(zhǔn)備工作做完以后IDEA里的代碼瀏覽和搜索就已經(jīng)可用了。但真正要跑起HDFS進(jìn)程還需要額外的運(yùn)行時(shí)配置我放到第6章再講。5. 遍歷macOS特有坑從glibtoolize到protoc版本5.1 glibtoolize和libtoolize的軟鏈問題這套源碼的native部分在macOS上編譯時(shí)第一個(gè)經(jīng)典報(bào)錯(cuò)來自autotools。CDH的configure.ac腳本會優(yōu)先找libtoolize命令但Linux發(fā)行版上的GNU libtool提供的是這個(gè)名字macOS上Homebrew安裝的libtool提供的是glibtoolize和glibtool因?yàn)橄到y(tǒng)里還有一個(gè)Apple版本的libtool用來做Mach-O庫管理的兩者沖突了。于是configure階段很容易出現(xiàn)這樣的錯(cuò)誤checking for libtoolize... no checking for glibtoolize... glibtoolize然后后續(xù)的Makefile生成過程會因?yàn)閘ibtool宏問題直接失敗。解決方式很直接做個(gè)軟鏈brew install libtool ln -s /usr/local/bin/glibtoolize /usr/local/bin/libtoolize ln -s /usr/local/bin/glibtool /usr/local/bin/libtool做完軟鏈后重新執(zhí)行Maven構(gòu)建這一步就能越過去。這個(gè)坑在Linux上的教程里基本見不到是macOS專屬。5.2 protoc版本被覆蓋Hadoop的native編譯過程中會調(diào)用protoc來生成一些協(xié)議代碼。如果你系統(tǒng)里有多個(gè)protobuf版本比如為了其他項(xiàng)目裝過protoc 3.x而且它的路徑在PATH中排在2.5.0之前那configure腳本會檢測到3.x版本并報(bào)版本不兼容的錯(cuò)誤。報(bào)錯(cuò)一般長這樣checking protoc version... 3.1.0 configure: error: cannot find compatible protoc遇到這個(gè)問題時(shí)不要急著改代碼先把PATH理順。我直接把protoc 2.5.0的bin目錄放在PATH最前面并在構(gòu)建命令前再次驗(yàn)證which protoc protoc --version確保輸出的是/usr/local/protobuf-2.5.0/bin/protoc和libprotoc 2.5.0然后再跑Maven構(gòu)建。這個(gè)問題的深層原因是CDH源碼的許多.proto文件生成的Java代碼是強(qiáng)綁定protoc 2.5.0的版本一旦漂移生成出來的源碼接口會變化后續(xù)javac編譯時(shí)會大量報(bào)錯(cuò)。你如果看到一堆cannot find symbol錯(cuò)誤先別急回頭檢查protoc版本比逐行改代碼靠譜得多。5.3 OpenSSL和snappy頭文件路徑當(dāng)啟用-Pnative構(gòu)建時(shí)configure腳本會探測OpenSSL、snappy、zlib、bzip2等依賴庫。macOS系統(tǒng)自帶的/usr/include/openssl在早期版本還存在但較新的Xcode Command Line Tools已經(jīng)把它移除了。如果你用的是12或13代的macOS建議在編譯前把Homebrew的openssl路徑導(dǎo)出到環(huán)境變量export OPENSSL_ROOT_DIR/usr/local/opt/openssl export OPENSSL_INCLUDE_DIR/usr/local/opt/openssl/include export OPENSSL_LIBRARY_DIR/usr/local/opt/openssl/lib export CPPFLAGS-I/usr/local/opt/openssl/include -I/usr/local/include export LDFLAGS-L/usr/local/opt/openssl/lib -L/usr/local/libsnappy的庫路徑同理。Hadoop的configure腳本在macOS上經(jīng)常找不到snappy.h和libsnappy.dylib導(dǎo)出CPPFLAGS和LDFLAGS是最省事的做法。如果你只是做Java層二開可以暫時(shí)不編進(jìn)native依賴把-Pnative去掉然后在運(yùn)行時(shí)使用-Djava.library.path指向不存在的目錄Hadoop會退回到純Java模式雖然會打warning但大部分HDFS測試功能可以跑。5.4 老插件和Maven 3.8的新仇舊賬我必須強(qiáng)調(diào)一下這個(gè)坑。CDH 5.14默認(rèn)pom里的maven-antrun-plugin版本很老舊插件在用模板引擎和資源過濾時(shí)對新版Maven的API兼容性非常差。如果你堅(jiān)持用Maven 3.8可能會遇到這樣的錯(cuò)誤[ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.7:run此時(shí)你有兩條路一條是像我一樣把Maven降級到3.3.9用CDH時(shí)代的工具鏈去編譯CDH時(shí)代的代碼。這條路的成本最低效果最快。另一條是在父pom里覆蓋插件版本比如把maven-antrun-plugin升到1.8或更高但風(fēng)險(xiǎn)是升級后的插件行為和舊腳本的預(yù)期不完全一致可能引入新的問題。我建議除非你對Maven插件機(jī)制十分熟悉否則還是降級Maven版本更穩(wěn)。這個(gè)問題的本質(zhì)是Hadoop 2.6年代還沒適配Maven 3.6之后引入的一些插件執(zhí)行細(xì)節(jié)。你非要讓老車跑新路也不是完全不行但前提是你愿意處理一長串連鎖反應(yīng)。6. 編譯產(chǎn)物檢查與本地偽分布式調(diào)試6.1 編譯完成后應(yīng)該出現(xiàn)哪些目錄當(dāng)mvn install全量通過后最好先檢查一下產(chǎn)物。在hadoop-dist/target下你會看到一個(gè)類似hadoop-2.6.0-cdh5.14.0的目錄這是-Pdist生成的可運(yùn)行發(fā)行目錄。里面包含bin/hdfs、yarn、mapred等啟動腳本etc/hadoop/默認(rèn)配置模板lib/Java依賴jarlib/native/本地庫目錄如果native編譯成功這里會有l(wèi)ibhadoop.dylib而不是Linux下的libhadoop.so如果你發(fā)現(xiàn)自己電腦上生成的是libhadoop.dylib別覺得奇怪macOS的動態(tài)庫后綴就是dylib。IDEA啟動NameNode時(shí)-Djava.library.path指向這個(gè)目錄就行。6.2 在IDEA里啟動NameNode和DataNode本地調(diào)試HDFS最常用的辦法是啟動兩個(gè)Java進(jìn)程N(yùn)ameNode和DataNode。在IDEA里我用Application類型的Run Configuration來跑具體配置如下。NameNodeMain class:org.apache.hadoop.hdfs.server.namenode.NameNodeVM options:-Djava.library.path/你的路徑/hadoop-dist/target/hadoop-2.6.0-cdh5.14.0/lib/nativeProgram arguments: 第一次運(yùn)行時(shí)先用-format之后用默認(rèn)參數(shù)啟動即可DataNodeMain class:org.apache.hadoop.hdfs.server.datanode.DataNodeVM options: 同上啟動前還需要設(shè)置環(huán)境變量HADOOP_CONF_DIR或準(zhǔn)備好一份core-site.xml。最簡單的做法是在IDEA的Run Configuration里加一個(gè)環(huán)境變量HADOOP_CONF_DIR/你的路徑/hadoop-dist/target/hadoop-2.6.0-cdh5.14.0/etc/hadoop用這個(gè)發(fā)行目錄自帶的etc/hadoop配置雖然默認(rèn)配置比較粗糙但足以讓NameNode和DataNode在本地跑起來。你要調(diào)試的二開代碼如果涉及某個(gè)特定配置項(xiàng)再單獨(dú)往配置文件里加property。6.3 native library加載問題本地調(diào)試時(shí)最不顯眼但又最常出問題的是native庫加載。Hadoop啟動日志如果出現(xiàn)WARN util.NativeCodeLoader: Unable to load native-hadoop library處理方式是用顯式的-Djava.library.path指定到lib/native目錄然后再次啟動。如果還不行就檢查這個(gè)目錄下是否真的生成了libhadoop.dylib有時(shí)候-Pnative沒啟用或者native構(gòu)建失敗這個(gè)目錄是空的。從IDEA啟動時(shí)VM options里的路徑不要有中文或空格否則JNI的加載邏輯會非常脆弱。我當(dāng)時(shí)把整個(gè)工程放在/Users/me/work/cdh-hadoop下路徑干干凈凈就是不想在這種地方踩低級坑。6.4 調(diào)試時(shí)的常見斷點(diǎn)位置如果你和我一樣目的是分析NameNode的內(nèi)存或元數(shù)據(jù)管理邏輯推薦關(guān)注這幾個(gè)類的斷點(diǎn)org.apache.hadoop.hdfs.server.namenode.FSNamesystem幾乎所有元數(shù)據(jù)操作的主戰(zhàn)場org.apache.hadoop.hdfs.server.blockmanagement.BlockManagerBlock的狀態(tài)機(jī)核心org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServerRPC請求入口打斷點(diǎn)時(shí)要注意NameNode進(jìn)程是一個(gè)持續(xù)運(yùn)行的daemon斷點(diǎn)打在啟動路徑上會在格式化階段就停住。建議先以-format參數(shù)跑一次格式化完成后再正常啟動否則斷點(diǎn)命中時(shí)機(jī)不對會讓你以為代碼有問題。把斷點(diǎn)打在NameNodeRpcServer的某個(gè)RPC方法上然后用HDFS的shell命令或一個(gè)簡單的Java客戶端去觸發(fā)mkdir、寫文件之類的操作就能觀察完整的調(diào)用鏈。這一套流程在IDEA里跑通以后本地二開的效率比寫代碼丟服務(wù)器驗(yàn)證高出一個(gè)數(shù)量級。最后說一點(diǎn)實(shí)在的整個(gè)過程最大的感受是不要把CDH的源碼當(dāng)成Apache Hadoop來編譯。版本對齊、倉庫配置、native工具的軟鏈處理哪一步都不能用“差不多”的心態(tài)去糊弄。我在x86 macOS上用IDEA跑通這套CDH 5.14編譯前后踩掉的坑如果算成時(shí)間夠我寫完十個(gè)業(yè)務(wù)模塊了。但一旦這條鏈路穩(wěn)定下來后續(xù)每天改代碼、跑進(jìn)程、斷點(diǎn)調(diào)試都變得非常順手。如果你也是被CDH釘在老版本上的開發(fā)希望這篇記錄能幫你少走一圈彎路。