HFile文件解析异常解决 1. 场景说明 2.异常分析 2.1 CRC32校验类找不到 2.2 文件block 找不到 2.3 NativeCodeLoader问题 3. 总结 参考

HFile文件解析异常解决
1. 场景说明
2.异常分析
2.1 CRC32校验类找不到
2.2 文件block 找不到
2.3 NativeCodeLoader问题
3. 总结
参考

需要对离线的 HFile 进行解析,默认可以使用如下的方式:

hbase org.apache.hadoop.hbase.io.hfile.HFile -f $HDFS_PATH -p -s

这样存在几个问题:

  1. 需要依赖 hbase 的环境
  2. -p 选项输出的结果,打印出来的不是 value 的值,里面默认使用的是 Bytes.toBinaryString
  3. 太重了,无关的内容不需要

所以需要对上面的实现方式改造一下。

阅读了源代码之后,了解到 HFile 里面包含的 main 方法初始化了一个 HFilePrettyPrinter 对象,把外部的参数传递给 HFilePrettyPrinter 的对象执行具体的工作。类图如下:

HFile文件解析异常解决
1. 场景说明
2.异常分析
2.1 CRC32校验类找不到
2.2 文件block 找不到
2.3 NativeCodeLoader问题
3. 总结
参考

从道理上,只需要把 HFilePrettyPrinter 的代码精简一下,就可以直接拿来用。

开始的时候,为了测试的方便,直接使用 hbase -jar $CLASSNAME args 来做验证,都是 ok 的。
输出的日志如下(后续会通过日志输出比对调试):

INFO  hfile.CacheConfig: Allocating LruBlockCache with maximum size 1.4g
INFO  blobstore.BlobFileCache: BlobFileCache Initialize
INFO  util.ChecksumType: Checksum using org.apache.hadoop.util.PureJavaCrc32
INFO  util.ChecksumType: Checksum can use org.apache.hadoop.util.PureJavaCrc32C
WARN  hdfs.DFSClient: Short circuit access failed
INFO  util.NativeCodeLoader: Trying to load the custom-built native-hadoop library...
INFO  util.NativeCodeLoader: Loaded the native-hadoop library
WARN  snappy.LoadSnappy: Snappy native library is available
INFO  snappy.LoadSnappy: Snappy native library loaded
INFO  compress.CodecPool: Got brand-new decompressor

但是一旦脱离了 hbase (/usr/lib/hbase/bin/hbase)这个宿主,直接运行 main 方法,总是报错。

2.异常分析

2.1 CRC32校验类找不到

最开始出现的是如下的错误:

INFO  org.apache.hadoop.hbase.io.hfile.CacheConfig - Allocating LruBlockCache with maximum size 1.4g
INFO  org.apache.hadoop.hbase.blobstore.BlobFileCache - BlobFileCache Initialize
INFO  org.apache.hadoop.hbase.util.ChecksumType - org.apache.hadoop.util.PureJavaCrc32 not available.
INFO  org.apache.hadoop.hbase.util.ChecksumType - Checksum can use java.util.zip.CRC32
INFO  org.apache.hadoop.hbase.util.ChecksumType - org.apache.hadoop.util.PureJavaCrc32C not available.
INFO  org.apache.hadoop.hdfs.DFSClient - Failed to read block blk_-2063727887249910869_1998268 on local machineorg.apache.hadoop.ipc.RPC$VersionMismatch: Protocol org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol version mismatch. (client = 4, server = 5)
        at org.apache.hadoop.ipc.RPC.getProxy(RPC.java:401)
        at org.apache.hadoop.ipc.RPC.getProxy(RPC.java:370) 
        at org.apache.hadoop.hdfs.DFSClient.createClientDatanodeProtocolProxy(DFSClient.java:174) 
        at org.apache.hadoop.hdfs.BlockReaderLocal$LocalDatanodeInfo.getDatanodeProxy(BlockReaderLocal.java:89) 
        at org.apache.hadoop.hdfs.BlockReaderLocal$LocalDatanodeInfo.access$200(BlockReaderLocal.java:64) 
        at org.apache.hadoop.hdfs.BlockReaderLocal.getBlockPathInfo(BlockReaderLocal.java:211) 
        at org.apache.hadoop.hdfs.BlockReaderLocal.newBlockReader(BlockReaderLocal.java:133) 
        at org.apache.hadoop.hdfs.DFSClient.getLocalBlockReader(DFSClient.java:358) 

关键部分是org.apache.hadoop.util.PureJavaCrc32 not available,这个问题比较明显,是lib 包中缺少必要的类。
为了避免类似的情况,直接用 hbase classpath 把打印出默认的依赖关系,然后把所有的依赖 jar 都加进来。

2.2 文件block 找不到

添加完 jar 包后,重新运行,还是报错,关键点是:
Failed to read block blk_-2063727887249910869_1998268 on local machineorg.apache.hadoop.ipc.RPC$VersionMismatch: Protocol org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol version mismatch. (client = 4, server = 5)

这里在分析的时候犯了一个错误:首先注意到的是 Failed to read block ... on local machine,而且在日志里面,后面会不断地从本地节点的 datanode 重试,直到超时;且在后面的日志中有 org.apache.hadoop.hdfs.DFSClient.getLocalBlockReader 的信息,莫名其妙地怎么会使用了 LocalBlockReader 呢?

开始就顺着这个思路去搜,找到了HDFS-2757,症状有点像,一看是个老版本的 bug,心说这下操蛋了。
但是显然我只是在读,没有写文件啊?再考虑下,既然使用 hbase 调起的方式可以正常使用,就证明现在的版本是可行的,可能这不是主要问题。

再看后面,Protocol org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol version mismatch. (client = 4, server = 5),之前没有写过 RPC 的程序,在 Google 上搜索了一下,从这里这里了解到,这种是典型的 RPC 协议版本不一致的问题。

特别是上面的作者在回复提问的时候,先是确认了 hadoop 的版本,故我转去确认了 lib 库中 hadoop 相关 jar 的版本,结果发现如下:

HFile文件解析异常解决
1. 场景说明
2.异常分析
2.1 CRC32校验类找不到
2.2 文件block 找不到
2.3 NativeCodeLoader问题
3. 总结
参考 这个确实没招了。。。之前使用不仔细造成的。

于是把 lib 包里面原有的全部清空,然后重新导入 hbase classpath 的输出jar。

2.3 NativeCodeLoader问题

上一步处理完之后,再次运行,还是报错:

INFO  org.apache.hadoop.hbase.io.hfile.CacheConfig - Allocating LruBlockCache with maximum size 1.4g
INFO  org.apache.hadoop.hbase.blobstore.BlobFileCache - BlobFileCache Initialize
INFO  org.apache.hadoop.hbase.util.ChecksumType - Checksum using org.apache.hadoop.util.PureJavaCrc32
INFO  org.apache.hadoop.hbase.util.ChecksumType - Checksum can use org.apache.hadoop.util.PureJavaCrc32C
WARN  org.apache.hadoop.hdfs.DFSClient - Short circuit access failed
INFO  org.apache.hadoop.util.NativeCodeLoader - Trying to load the custom-built native-hadoop library...
ERROR org.apache.hadoop.util.NativeCodeLoader - Failed to load native-hadoop with error: java.lang.UnsatisfiedLinkError: no hadoop in java.library.path
ERROR org.apache.hadoop.util.NativeCodeLoader - java.library.path=/usr/java/jdk1.6.0_31/jre/lib/amd64/server:/usr/java/jdk1.6.0_31/jre/lib/amd64:/usr/java/jdk1.6.0_31/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
WARN  org.apache.hadoop.util.NativeCodeLoader - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
ERROR org.apache.hadoop.io.compress.snappy.LoadSnappy - Failed to load snappy with error: java.lang.UnsatisfiedLinkError: no snappy in java.library.path
WARN  org.apache.hadoop.io.compress.snappy.LoadSnappy - Snappy native library not loaded
Exception in thread "main" java.lang.RuntimeException: native snappy library not available
        at org.apache.hadoop.io.compress.SnappyCodec.getDecompressorType(SnappyCodec.java:189)
        at org.apache.hadoop.io.compress.CodecPool.getDecompressor(CodecPool.java:138)
        at org.apache.hadoop.hbase.io.hfile.Compression$Algorithm.getDecompressor(Compression.java:290)

关键信息是:

  1. no hadoop in java.library.path
  2. no snappy in java.library.path

这里从新去 /usr/lib/hbase/bin/hbase 文件中查看了下,发现里面有对 $JAVA_PLATFORM$JAVA_LIBRARY_PATH 这两个环境变量的整理。

这里 sudo 修改了 hbase 的执行文件,中间把上面两个环境变量打印出来。第一个 $JAVA_PLATFORM 显示的是 Linux-amd64-64,第二个是/usr/lib/hadoop/lib/native/Linux-amd64-64文件夹的内容。

然后把 $JAVA_LIBRARY_PATH的内容,通过 -Djava.library.path= 的方式,添加到 Eclipse 的 VM Variables 中。

再次运行,正常。

3. 总结

花了一个下午的时间来 DEBUG 这个异常,回过头看,有以下几点需要注意:

  1. 要认真读日志,通过打印出来的错误堆栈,分清楚哪些是真正错误的源头,哪些是错误扩散的结果。比如花了半天研究 HDFS-2757,后来发现没关系,这个就很没有必要;
  2. 认识到 hbase、hadoop 这种工具,除了看得到的组件代码之后,看不到额环境配置等内容,也很重要,没有合适的环境配置,正确的代码也是没法正常运行的,需要进一步花一点时间了解组件的运行时环境的配置需求;
  3. 排错是个厚积薄发的事情,遇到大小问题都要好好思考,多看源代码,解决起来才会有思路。

参考

  1. HDFS-2757
  2. RPC 报错问答1
  3. RPC 报错问答2