Hadoop HAR文件的读取操作

概述

Hadoop Archive是Hadoop官方提供的解决HDFS上小文件过多的一种方案。可以通过如下命令来执行生成har文件:

执行archive命令会提交一个MapReduce任务来生成har文件。在了解har文件结构后也可以考虑本地生成har文件再上传。

关于“hadoop archive”指令的更多细节请参考官方文档

HAR文件结构

HAR文件实际上是一个以”.har”结尾命名的目录,其中至少包含三个文件:

  1. _index
  2. _masterindex
  3. part-00000 …

其中“_index”文件中存储包内目录、文件的元数据信息,并按路径java字符串hashCode()运算的哈希值排序。

“_masterindex”记录了“_index”文件中每1000条文件元数据信息的起止哈希值、以及其在“_index”文件中的起止位置。

“part-X”文件中直接拼接了原始文件内容,无压缩处理,每个map操作的节点生成一个“part-X”文件。

HAR文件读取

在平时工作中读取hdfs文件有三种形式:即在java代码中通过hadoop-client形式读取,执行spark任务读取,通过webhdfs的rest api读取。下面一一介绍下相关的的解决方案。

通过java-client方式读取

假设我们有一个har归档文件,其存储目录是:/har/hp2.har。

在这个归档文件下存在一个目录hp,存储了7个txt文件:

下面通过hadoop-client编写程序列出“har:///har/hp2.har/hp/”下的全部文件:

上面是用scala代码做的实现。

比照普通的HDFS文件的访问方式,访问Har文件的主要特点在于其FileSystem操作对象是一个HarFileSystem的实例。

HarFileSystem实例执行initialize()操作的时候需要传入要访问的har文件的根路径,其后所有的操作都是对har子项的相对路径进行操作。

注意:执行initialize()操作时只能传入har文件的根路径,不能像执行上面的“hfs -ls har:///har/hp2.har/hp/”指令一样传入一个完整的har子项的路径。

通过spark任务读取

spark读取数据文件大致有三种形式:

  1. 通过SparkSession.read.textFile读取文件创建DataSet
  2. 通过SparkSession.read.text读取文件创建DataFrame
  3. 通过SparkContext.textFile读取文件创建RDD

针对这三种形式,我分别写了一段代码进行验证:

在执行日志中输出的验证信息如下:

根据执行结果可知,通过SparkSession读取的两种方案均不可行,只有通过SparkContext进行读取才能达到预期效果。

通过webhdfs方式读取

关于直接通过webhdfs方式读取har中内容的方式,我查了些资料,包括Hadoop官方文档,StackOverflow的相关话题以及Google检索出的条目,其中勉强可行的是如下两篇文章建议的方案:

其实现思路大致如下:

  1. 通过webhdfs获取har的index文件
  2. 在index文件中找到在HDFS中存储目标文件的数据文件,以及目标文件在数据文件中的起始offset及长度
  3. 通过webhdfs获取目标文件:http://<hadoop-server>:50070/webhdfs/v1/data-file-path?op=OPEN&offset=$offset&length=$len

扫描har index文件并截取内容不太像是一种优雅的做法,至少我并不喜欢。

简单介绍一种替代方案,即通过hadoop-client实现数据流拷贝,下面是代码实现:

在api接口中调用copy方法即可实现下载功能。代码稍稍有些多,但窃以为还是要比扫描index好一些。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据