在实现《Hadoop高级编程》一书中第9章的一个例子:将HBase用于图片管理系统
中,遇到了一个很让人头疼的问题:FileNotFoundException
。虽然异常很简单,但是文件确实存在那了。于是开始了长时间的排错之旅。
该例子是将一堆小图片文件合成一个大文件,并将各个小文件的位置索引存入HBase中。有一个DatedPhoto
类,用于存放时间(long
)以及图片(byte[]
);还有一个PhotoLocation
类,用于存放位置(long
)、时间(long
)和文件名(String
)。位置是小文件在大文件中的字节位置,文件名是合成的大文件名,按序号命名。PhotoLocation
类还提供了toBytes
和fromBytes
函数,用于将三种信息字节化后写入HBase以及从HBase中读出索引信息后还原成位置, 时间 和 文件名 信息。其余为写入类,读出类。整体非常简单。
写的时候完全没有问题,文件成功生成,HBase中保存了索引。但是读取的时候,出现问题:一直报FileNotFoundException
异常,而路径并未出错而且文件存在。
这到底是怎么一回事呢?因为用的是SequenceFile
中的内部类Reader
和Writer
,而Hadoop 2.2.0
的API中并没有找到这两个东西,便从源码入手看看是不是这个类的问题。按照执行流程过了一遍,任何问题都没发现。由于错误定位在SequenceFile.Reader
的构造函数中getFileStatus
上,便重新写了一个类测试FileSystem
类的getFileStatus
类,发现完全没有问题,不是这出的错误。
PhotoDataReader
类是辅助读出数据的类,被PhotoReader
类调用。错误就定位在了该类的构造上。查看PhotoDataReader
类构造函数,其功能就是接受参数,调用SequenceFile.Reader
的构造函数。输出PhotoDataReader
接收到的参数:file
(String
)、user
(UUID
)、conf
(Configuration
),三者都没问题,但是出现了一个很奇怪的现象:
执行
System.out.println("file:"+ _file + "sadasdsasdssssss");
时,后面的那一串字符串居然不显示!
看来问题就出现在_file
参数上面。但是不管怎么输出_file
的值,的确就是我要的那个文件名的字符串。似乎这是个根本就不应该出现的问题。然后输出_file
的字符串长度,明明只有1个字符的文件名,显示的长度为112!看来问题就在这!
问题究竟出现在哪呢?继续向上找,发现参数是PhotoReader
传给它的;PhotoReader
又通过读取HBase中的索引记录得到后用PhotoLocation
的fromBytes
函数得到…… fromBytes
函数?其实现的是将一个包含索引位置 、时间 与 文件名的128位字符数组分开然后生成三个值。索引位置与时间均为long
型,字符数组均为8位,没有问题。但是文件名为字符串,合成字符数组时用的length
函数,但是还原时就把除索引位置与时间的16位排除后剩下的112位全部用来生成字符串了!生成字符串用Bytes.bytes()
和 new String
均无法生成原长度的字符串。
既然知道了原因,解决也好办了。要么设个长度位,要么用定长字符串,要么干脆在HBase分开存储。看来,有时候报错,问题往往出现在细节上,仔细思考每一处细节,可以减少很多的排错时间。至少这次我花了不少时间。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。