hadoop源码中loadFSImage中的一个疑似bug

    学习hadoop源码的过程中,遇到这么一个问题,给大家分享如下。为什么说是“疑似”,相信大家在使用hadoop的时候,大部分情况不会遇到这个问题,但是在我的实验环境中确实存在这个问题。

    问题描述:

    hadoop版本:1.0.3

    在实验环境下,格式化namenode后,启动namenode(运行时参数 -regular)报错:

2013-01-07 16:13:04,567 ERROR - FSNamesystem.<init>(348) | FSNamesystem initialization failed.
java.io.IOException: Unexpected block size: -1
	at com.test.hadoop.hdfs.protocol.Block.readFields(Block.java:128)
	at com.test.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:920)
	at com.test.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:811)
	at com.test.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:358)
	at com.test.hadoop.hdfs.server.namenode.FSDirectory.loadFSImage(FSDirectory.java:90)
	at com.test.hadoop.hdfs.server.namenode.FSNamesystem.initialize(FSNamesystem.java:372)
	at com.test.hadoop.hdfs.server.namenode.FSNamesystem.<init>(FSNamesystem.java:346)
	at com.test.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:264)
	at com.test.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:519)
	at com.test.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1307)
	at com.test.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1316)
2013-01-07 16:13:04,568 ERROR - NameNode.main(1320) | java.io.IOException: Unexpected block size: -1
	at com.test.hadoop.hdfs.protocol.Block.readFields(Block.java:128)
	at com.test.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:920)
	at com.test.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:811)
	at com.test.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:358)
	at com.test.hadoop.hdfs.server.namenode.FSDirectory.loadFSImage(FSDirectory.java:90)
	at com.test.hadoop.hdfs.server.namenode.FSNamesystem.initialize(FSNamesystem.java:372)
	at com.test.hadoop.hdfs.server.namenode.FSNamesystem.<init>(FSNamesystem.java:346)
	at com.test.hadoop.hdfs.server.namenode.NameNode.initialize(NameNode.java:264)
	at com.test.hadoop.hdfs.server.namenode.NameNode.<init>(NameNode.java:519)
	at com.test.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1307)
	at com.test.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1316)
    问题分析:

    代码运行到Block.java:128这个地方就报错了,Block.readFields这个方法的作用是读取文件块的内容,同时,如果读取的字节数小于0,就会报错,这也是为什么我启动namenode的时候会报错。Block.readFields的方法给我们的提示信息仅仅就是没有读到块数据,没有多大的参考意义。检查FSImage.loadFSImage方法,在该方法会发现有这么一段代码:

if ((-9 <= imgVersion && numBlocks > 0) ||(imgVersion < -9 && numBlocks >= 0)) {
          blocks = new Block[numBlocks];
          for (int j = 0; j < numBlocks; j++) {
            blocks[j] = new Block();
            if (-14 < imgVersion) {
              blocks[j].set(in.readLong(), in.readLong(), 
                            Block.GRANDFATHER_GENERATION_STAMP);
            } else {
              blocks[j].readFields(in);
            }
          }
        }
    大致意思就是当满足 (-9 <= imgVersion && numBlocks > 0) || (imgVersion < -9 && numBlocks >= 0) 条件下才执行 blocks[j].readFields(in) 代码,而(-9 <= imgVersion && numBlocks > 0) || (imgVersion < -9 && numBlocks >= 0)的意图是为了保证有可读的块,综合这段分析,报错的原因很可能是因为,Inode所在的目录本身是没有block,由于某些异常原因,又执行了blocks[j].readFields(in)这段代码。因为我刚刚执行了namenode的格式化操作,Fsimage中目前只有rootDir根目录的信息,在格式化namenode的时候,根目录的block块数是-1,这意味着blocks[j].readFields(in) 这段代码理论上是不可能执行的,实际上他又执行了,这样的话numBlocks很可能出问题了。进一步检查变量numBlocks的值发现发现是16777215,而不是-1。

    综合上面的分析,我怀疑是loadFSImage方法中,解析fsimage文件出错,下面例子更加证明了我的想法。

DataInputStream in = new DataInputStream(
				new BufferedInputStream(new FileInputStream(
						"E:\\data\\hadoop\\name\\current\\fsimage")));

		// read image version: first appeared in version -1
		int imgVersion = in.readInt();
		// read namespaceID: first appeared in version -2
		int namespaceID = in.readInt();

		// read number of files
		long numFiles = in.readLong();

		// read in the last generation stamp.
		long genstamp = in.readLong();

		short namelength = in.readShort();
		byte[] data = new byte[namelength];
		in.readFully(data, 0, namelength);
		
		String path = new String(data);
		short replication = in.readShort();
		long modificationTime = in.readLong();
		long atime = in.readLong();
		long blockSize = in.readLong();
		int numBlocks = in.readInt();  // 实际值也是-1
		
		long nsQuota = in.readLong();
		long dsQuota = in.readLong();
上面这段代码直接读取fsimage文件,对应fsimage的持久化过程,读取fsimage中的值。在查看这些具体的值后,我确信fsimage文件是没有问题的。由于在loadFSImage方法和我的例子中,path变量的读取存在差异,然后替换过去,启动namenode,问题解决。

    总结:

  •     写short数据:DataOutputStream.writeShort(nameLen); 这里用了两个byte
  •     读short数据:WritableUtils.readVLong(DataInput stream) ,这里读nameLen时,只用了一个byte
这样就出问题了。

转载于:https://my.oschina.net/psuyun/blog/100624

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值