记一次Hadoop1.0.4集群的事故

由于历史原因,公司内部还在使用hadoop1.0.4版本的集群,而且上面运行的任务还挺多。当初开发这套系统的员工几乎都离职了,所以这块成了鸡肋。撤掉吧,上面还有好的用户;迁移吧,涉及的东西太多,影响比较大。所以这套系统就只能一直这样维护着。

终于在一次集群的大事故中,让大家认识到,再也不能用hadoop1.0集群了。

一、问题的原因

Hadoop1.0的HDFS元数据是存放在fsimage中的,编辑日志存放在edits;SecondaryNameNode节点负责把edits日志合并到fsimage中,用于数据恢复。当遇到edits日志中存在异常的时候,元数据不再往edits文件中写,而是写入edits.new文件中。当发现这个问题的时候,就需要在hdfs的安全模式下,使用以下命令进行恢复

hadoop dfsadmin -saveNamespace

但是在没恢复之前,如果重启namenode节点,问题就大了。我们这边由于某个mr采用多路径输出,把中文输出到文件路径中了,导致在元数据中存放了乱码,启动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.(FSNamesystem.java:362)
    at org.apache.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:276)
    at org.apache.hadoop.hdfs.server.namenode.NameNode.(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文件

由于系统是hadoop1.0.4所以不存在 hdfs 这个命令,只好把文件拷贝到hadoop2.6.0上进行操作。

打开转换后的xml文件,把存在乱码的Recode标签内的记录都删除掉,然后在转换为二进制文件。

当解析 image 文件的时候,发现解析不了,查看资料才发现,原来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节点的对应目录下,重启namenode进程,终于能正常启动了。

三、总结

1、不能在edits.new存在的情况下,重启namenode进程

2、hdfs中的路径不能存在乱码,最后就用字母数字下划线这些比较通用的字符来做路径

你可能感兴趣的:(记一次Hadoop1.0.4集群的事故)