由于歷史原因们妥,公司內(nèi)部還在使用hadoop1.0.4版本的集群猜扮,而且上面運行的任務還挺多。當初開發(fā)這套系統(tǒng)的員工幾乎都離職了监婶,所以這塊成了雞肋旅赢。撤掉吧,上面還有好的用戶惑惶;遷移吧煮盼,涉及的東西太多,影響比較大带污。所以這套系統(tǒng)就只能一直這樣維護著僵控。
終于在一次集群的大事故中,讓大家認識到鱼冀,再也不能用hadoop1.0集群了报破。
一、問題的原因
Hadoop1.0的HDFS元數(shù)據(jù)是存放在fsimage中的雷绢,編輯日志存放在edits泛烙;SecondaryNameNode節(jié)點負責把edits日志合并到fsimage中理卑,用于數(shù)據(jù)恢復翘紊。當遇到edits日志中存在異常的時候,元數(shù)據(jù)不再往edits文件中寫藐唠,而是寫入edits.new文件中帆疟。當發(fā)現(xiàn)這個問題的時候鹉究,就需要在hdfs的安全模式下,使用以下命令進行恢復
hadoop dfsadmin -saveNamespace
但是在沒恢復之前踪宠,如果重啟namenode節(jié)點自赔,問題就大了。我們這邊由于某個mr采用多路徑輸出柳琢,把中文輸出到文件路徑中了绍妨,導致在元數(shù)據(jù)中存放了亂碼,啟動namenode的時候柬脸,fsimage一直檢查不通過他去,異常如下:
ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: java.io.IOException: Found lease for non-existent file /data/houraggregate/eventself_day/2017/05/01/14/output/_temporary/_attempt_201602020826_208092_r_000006_0/part-r-00006-DESelf_Coi#@$%^Pd
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFilesUnderConstruction(FSImage.java:1440)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:986)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:830)
at org.apache.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:377)
at org.apache.hadoop.hdfs.server.namenode.FSDirectory.loadFSImage(FSDirectory.java:100)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.initialize(FSNamesystem.java:388)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.<init>(FSNamesystem.java:362)
at org.apache.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:276)
at org.apache.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:496)
at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1279)
at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1288)
二、處理過程
既然是edit文件出異常倒堕,就應該修復該文件灾测。查看資料是可以通過以下命令把edit這個二進制文件解析成xml文件的
hdfs oev -i edits -o edits.xml //解析edits文件
hdfs oiv -i fsimage -o fsimage.xml //解析fsimage文件
解析完成后通過以下命令重新轉回二進制文件
hdfs oev -i edits.xml -o edits -p binary //轉換xml文件成edits文件
hdfs oiv -i fsimage.xml-o fsimage -p binary //轉換xml文件成fsimage文件
由于系統(tǒng)是hadoop1.0.4所以不存在 hdfs 這個命令,只好把文件拷貝到hadoop2.6.0上進行操作垦巴。
打開轉換后的xml文件媳搪,把存在亂碼的Recode標簽內(nèi)的記錄都刪除掉,然后在轉換為二進制文件骤宣。
當解析 image 文件的時候秦爆,發(fā)現(xiàn)解析不了,查看資料才發(fā)現(xiàn)涯雅,原來fsimage文件是與hadoop版本一一對應的鲜结。
所以上面所做的所有工作都是無效的。
后面想到活逆,能不能通過查看hadoop寫fsimage文件的過程精刷,然后反過來解析呢?但是這樣處理蔗候,會耗費大量的時間去閱讀源碼怒允,實際情況是不允許的。轉念一想锈遥,能不能把判斷亂碼的代碼修改為纫事,如果遇到就跳過呢?
通過查看異常的堆棧所灸,找到是 FSImage 類的以下代碼報出的異常
for (int i = 0; i < size; i++) {
INodeFileUnderConstruction cons = readINodeUnderConstruction(in);
// verify that file exists in namespace
String path = cons.getLocalName();
INode old = fsDir.getFileINode(path);
if (old == null) {
// 從此處報出異常
throw new IOException("Found lease for non-existent file " + path);
}
if (old.isDirectory()) {
throw new IOException("Found lease for directory " + path);
}
INodeFile oldnode = (INodeFile) old;
fsDir.replaceNode(path, oldnode, cons);
fs.leaseManager.addLease(cons.clientName, path);
}
把代碼修改如下:
for (int i = 0; i < size; i++) {
INodeFileUnderConstruction cons = readINodeUnderConstruction(in);
// verify that file exists in namespace
String path = cons.getLocalName();
// 添加判斷丽惶,把存在異常的路徑過濾掉
if (path.contains("_temporary")){
continue;
}
INode old = fsDir.getFileINode(path);
if (old == null) {
throw new IOException("Found lease for non-existent file " + path);
}
if (old.isDirectory()) {
throw new IOException("Found lease for directory " + path);
}
INodeFile oldnode = (INodeFile) old;
fsDir.replaceNode(path, oldnode, cons);
fs.leaseManager.addLease(cons.clientName, path);
}
然后編譯FSImage類,打包到hadoop-core-1.0.4.jar中爬立,放入hadoop1.0.4 namenode節(jié)點的對應目錄下钾唬,重啟namenode進程,終于能正常啟動了。
三抡秆、總結
1奕巍、不能在edits.new存在的情況下,重啟namenode進程
2儒士、hdfs中的路徑不能存在亂碼的止,最后就用字母數(shù)字下劃線這些比較通用的字符來做路徑