在实现《Hadoop高级编程》一书中第9章的一个例子:将HBase用于图片管理系统中,遇到了一个很让人头疼的问题:FileNotFoundException。虽然异常很简单,但是文件确实存在那了。于是开始了长时间的排错之旅。

该例子是将一堆小图片文件合成一个大文件,并将各个小文件的位置索引存入HBase中。有一个DatedPhoto类,用于存放时间(long)以及图片(byte[]);还有一个PhotoLocation类,用于存放位置(long)、时间(long)和文件名(String)。位置是小文件在大文件中的字节位置,文件名是合成的大文件名,按序号命名。PhotoLocation类还提供了toBytesfromBytes函数,用于将三种信息字节化后写入HBase以及从HBase中读出索引信息后还原成位置, 时间文件名 信息。其余为写入类,读出类。整体非常简单。

写的时候完全没有问题,文件成功生成,HBase中保存了索引。但是读取的时候,出现问题:一直报FileNotFoundException异常,而路径并未出错而且文件存在。

这到底是怎么一回事呢?因为用的是SequenceFile中的内部类ReaderWriter,而Hadoop 2.2.0的API中并没有找到这两个东西,便从源码入手看看是不是这个类的问题。按照执行流程过了一遍,任何问题都没发现。由于错误定位在SequenceFile.Reader的构造函数中getFileStatus上,便重新写了一个类测试FileSystem类的getFileStatus类,发现完全没有问题,不是这出的错误。

PhotoDataReader类是辅助读出数据的类,被PhotoReader类调用。错误就定位在了该类的构造上。查看PhotoDataReader类构造函数,其功能就是接受参数,调用SequenceFile.Reader的构造函数。输出PhotoDataReader接收到的参数:fileString)、userUUID)、confConfiguration),三者都没问题,但是出现了一个很奇怪的现象:

执行

    System.out.println("file:"+ _file + "sadasdsasdssssss");

时,后面的那一串字符串居然不显示!

看来问题就出现在_file参数上面。但是不管怎么输出_file的值,的确就是我要的那个文件名的字符串。似乎这是个根本就不应该出现的问题。然后输出_file的字符串长度,明明只有1个字符的文件名,显示的长度为112!看来问题就在这!

问题究竟出现在哪呢?继续向上找,发现参数是PhotoReader传给它的;PhotoReader又通过读取HBase中的索引记录得到后用PhotoLocationfromBytes函数得到…… fromBytes函数?其实现的是将一个包含索引位置时间文件名的128位字符数组分开然后生成三个值。索引位置与时间均为long型,字符数组均为8位,没有问题。但是文件名为字符串,合成字符数组时用的length函数,但是还原时就把除索引位置与时间的16位排除后剩下的112位全部用来生成字符串了!生成字符串用Bytes.bytes()new String均无法生成原长度的字符串。

既然知道了原因,解决也好办了。要么设个长度位,要么用定长字符串,要么干脆在HBase分开存储。看来,有时候报错,问题往往出现在细节上,仔细思考每一处细节,可以减少很多的排错时间。至少这次我花了不少时间。


psy
392 声望12 粉丝

假装平静的外表下一颗躁动的心