hadoop出現元資料不能更新且SNN合併失效
阿新 • • 發佈:2019-02-02
問題表現: NameNode 儲存edits檔案 停留在5.3號凌晨。SNN執行合併檔案報 空指標錯誤,導致無法正常合併元資料
原因:要弄清原因首先需要清楚SNN合併流程,NN寫editslog流程等等。簡單說來如下:
1 在5.3號 SNN合併檔案後併成功將合併的資料put到NN。當NN在關閉臨時edit檔案edit.new,開啟edits檔案時報錯:unable to open
2 正常情況下,開啟edits檔案後,會將edits輸出流加入前面已經清空的edits輸出流列表。在1 失敗下,該操作未做,所以edits輸出流列表為空表物件
3 開啟失敗下,會將失敗的路徑自動移除掉,以便後面的自動嘗試恢復。同時自動嘗試恢復是在SNN合併檔案時出發。但是咱們的情況是2個edits路徑全部失敗,導致 路徑集合為null。
4 新的SNN合併請求過來後會先得到儲存路徑集合,此時報Null退出 。
5 對於檔案操作的日誌,因為2操作沒有執行,沒有可用的edits輸出流列表,所以直接往下執行其他操作,導致edits檔案也不再更新。
解決思路:
1 因為NN不再更新edits檔案,SNN也無法合併Img,所以以前NN儲存的元資料無法使用。所以必須恢復元資料到最新。這可以通過hdfs提供的api匯出hdfs目錄元資料並儲存。
這個操作必須在安全模式下執行。
2 從上面5點看,很多操作時由於打不開edit後導致的edits輸出流列表為空表(不是Null)和NN的元資料儲存路徑為Null
3 恢復edits輸出流列表,才能讓NN正常寫edits,這個操作可以呼叫hdfs提供的api實現。
4 恢復NN的元資料儲存路徑也可以呼叫hdfs提供的api重新設定。
解決方案:
1 讓叢集進入安全模式,使用匯出元資料的jsp ,匯出最新元資料,停止叢集,,拷貝新的元資料替換舊的元資料後重啟叢集。
2 讓叢集進入安全模式,使用匯出元資料的jsp ,匯出最新元資料,拷貝新的元資料替換舊的元資料。恢復edits流列表,恢復NN的元資料儲存路徑列表,離開安全模式。(測試正常,但沒在線上應用)
附件:
1 匯出元資料的jsp:
2 檢視 edit流列表及儲存路徑集合及其他物件jsp:
3 恢復 edits流列表jsp:
4 恢復NN儲存路徑列表jsp:
jsp執行 前 請將jsp放到webapp/hdfs下 。
<%@ page contentType="text/html; charset=UTF-8" isThreadSafe="false" import="java.io.*" import="java.lang.reflect.*" import="org.apache.hadoop.hdfs.*" import="org.apache.hadoop.hdfs.server.namenode.*" import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory" import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirType" %> <% String path = request.getParameter("dir"); if (path == null) { throw new IllegalArgumentException("specify dir parameter"); } File dir = new File(path); if (!dir.exists()) { dir.mkdir(); } NameNode nn = (NameNode)application.getAttribute("name.node"); if (!nn.isInSafeMode()) { throw new IllegalStateException("not in safe mode"); } // Use reflection to find saveCurrent() FSImage image = nn.getFSImage(); Method m = FSImage.class.getDeclaredMethod("saveCurrent", StorageDirectory.class); m.setAccessible(true); // Use reflection to find the IMAGE_AND_EDITS enum, since it's package-protected Class c = Class.forName("org.apache.hadoop.hdfs.server.namenode.FSImage$NameNodeDirType"); StorageDirType[] constants = (StorageDirType[])c.getEnumConstants(); StorageDirType t = null; for (StorageDirType sdt : constants) { if (sdt.toString().equals("IMAGE_AND_EDITS")) { t = sdt; } } if (t == null) { throw new IllegalStateException("no type"); } // Actually save StorageDirectory sd = image.new StorageDirectory(dir, t); m.invoke(image, sd); %> Saved image to <%= sd.getCurrentDir() %>
2 檢視 edit流列表及儲存路徑集合及其他物件jsp:
<%@ page contentType="text/html; charset=UTF-8" isThreadSafe="false" import="java.io.*" import="java.util.*" import="java.lang.reflect.*" import="org.apache.hadoop.hdfs.*" import="org.apache.hadoop.hdfs.server.namenode.*" import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory" import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirType" %> <% NameNode nn = (NameNode)application.getAttribute("name.node"); out.println("namenode="+nn.toString()); final FSImage nnImage = (FSImage)application.getAttribute("name.system.image"); out.println("storagedirs="+nnImage.listStorageDirectories()); Method m = FSImage.class.getDeclaredMethod("getFsImageName", null); m.setAccessible(true); out.println("nnImage.getFsImageName()="+m.invoke(nnImage,null)); out.println("httpserver name.system.image="+nnImage.toString()); out.println("getFsImage from nn="+nn.getFSImage()); out.println("<br/>"); File eFile=new File("/data0/hadoop/hdfs/name/current/edits"); RandomAccessFile rp = new RandomAccessFile(eFile, "rw"); FileOutputStream fp = new FileOutputStream(rp.getFD()); // FSEditLog.EditLogOutputStream eStream = new FSEditLog.EditLogFileOutputStream(eFile); out.println("fileoutputstream="+fp.toString()); out.println("<br/>"); m = FSImage.class.getDeclaredMethod("getRemovedStorageDirs", null); m.setAccessible(true); List<StorageDirectory> list=(List<StorageDirectory>)m.invoke(nnImage,null); out.println("removedStorageDirs.size="+list.size()); for(StorageDirectory dir:list) out.println("removeddir="+dir.getRoot().getPath().toString()); out.println("<br/>"); FSNamesystem fsNamesystem=nn.getNamesystem(); Method mm = FSNamesystem.class.getDeclaredMethod("getEditLog", null); mm.setAccessible(true); FSEditLog editlog=(FSEditLog)mm.invoke(fsNamesystem,null); out.println("nn's editlog="+editlog.toString()); Method mm1 = FSEditLog.class.getDeclaredMethod("getNumEditStreams", null); mm1.setAccessible(true); out.println("getNumEditStreams="+mm1.invoke(editlog,null)); %>
3 恢復 edits流列表jsp:
<%@ page contentType="text/html; charset=UTF-8" isThreadSafe="false" import="java.io.*" import="java.util.*" import="java.lang.reflect.*" import="org.apache.hadoop.hdfs.*" import="org.apache.hadoop.hdfs.server.namenode.*" import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory" import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirType" %> <% NameNode nn = (NameNode)application.getAttribute("name.node"); FSNamesystem fsNamesystem=nn.getNamesystem(); Method mm = FSNamesystem.class.getDeclaredMethod("getEditLog", null); mm.setAccessible(true); FSEditLog editlog=(FSEditLog)mm.invoke(fsNamesystem,null); out.println("nn's editlog="+editlog.toString()); Method mm1 = FSEditLog.class.getDeclaredMethod("getNumEditStreams", null); mm1.setAccessible(true); out.println("getNumEditStreams="+mm1.invoke(editlog,null)); Field field=FSEditLog.class.getDeclaredField("editStreams"); field.setAccessible(true); ArrayList editStreams=(ArrayList)field.get(editlog); out.println(editStreams.size()); out.println("<br/>begin to reset editStreams..."); editStreams.clear(); Class c = Class.forName("org.apache.hadoop.hdfs.server.namenode.FSEditLog$EditLogFileOutputStream"); Constructor constructor=c.getDeclaredConstructor(File.class); constructor.setAccessible(true); File f=new File("/analyser/hdfs/dfs/name/current/edits"); editStreams.add(constructor.newInstance(f)); //f=new File("/data0/hadoop/aernfs/name/current/edits"); //editStreams.add(constructor.newInstance(f)); out.println("<br/> reset editStreams success!"); out.println("editStreams.size()="+editStreams.size()); out.println("getNumEditStreams="+mm1.invoke(editlog,null)); %>
4 恢復NN儲存路徑列表jsp:
<%@ page
contentType="text/html; charset=UTF-8"
isThreadSafe="false"
import="java.io.*"
import="java.util.*"
import="java.lang.reflect.*"
import="org.apache.hadoop.hdfs.*"
import="org.apache.hadoop.conf.*"
import="org.apache.hadoop.hdfs.server.namenode.*"
import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory"
import="org.apache.hadoop.hdfs.server.common.Storage.StorageDirType"
%>
<%
NameNode nn = (NameNode)application.getAttribute("name.node");
out.println("namenode="+nn.toString());
final FSImage nnImage = (FSImage)application.getAttribute("name.system.image");
out.println("storagedirs="+nnImage.listStorageDirectories());
Method m = FSImage.class.getDeclaredMethod("getFsImageName", null);
m.setAccessible(true);
out.println("nnImage.getFsImageName()="+m.invoke(nnImage,null));
out.println("<br/>begin resetStorageDirectories...");
Method m1=FSImage.class.getDeclaredMethod("setStorageDirectories", Collection.class,Collection.class);
m1.setAccessible(true);
Configuration conf = new Configuration();
m1.invoke(nnImage,FSNamesystem.getNamespaceDirs(conf),FSNamesystem.getNamespaceEditsDirs(conf));
out.println("<br/> resetStorageDirectories success!");
out.println("<br/>");
out.println("nnImage.getFsImageName()="+m.invoke(nnImage,null));
out.println("storagedirs="+nnImage.listStorageDirectories());
%>
jsp執行 前 請將jsp放到webapp/hdfs下 。