从零开始学习Hadoop--第6章 MapReduce的输入输出

    这一章都是文字叙述,不需要写源代码了。一般情况下,只需要记住这些东西就可以了。


Hadoop处理大数据。


大数据以文件的形式存储在HDFS


大文件被划分成文件块存贮,每个文件块有固定的大小,通常是64M,或者128M,或者255M


我们在第2章写了一个WordCountMapReduce程序,最关键部分是MapperReducer。在做MapReuce时,先做Map,再做ReduceHadoop的框架让我们不需要关注MapperReducer之外的地方,但我们肯定会想,数据是怎么从HDFS传给Mapper的?Reducer处理完数据之后,又是怎么将数据存储到HDFS的?


将数据从HDFS传到Mapper是由InputFormat类实现的。


将数据从Reducer存储到HDFS是由OutputFormat类实现的。


先解释一下Java的抽象类。抽象类就是声明了抽象函数的类。假如A是一个抽象类,它有一个抽象函数do_it,那么它只是声明有do_it这个函数,但并没有实现它的功能。抽象类只能作为类的模板,也就是说抽象类不能实例化。假设,类B继承了类AB也就有了dot_it函数,如果B是一个具体的类,它就必须在自己的内部实现do_it函数的功能。

1.InputFormat

InputFormat类是一个抽象类。InputFormat类定义了两个抽象函数。这两个抽象函数是:

abstract List<InputSplit> getSplits(JobContext context) ”

abstract RecordReader<K,V> createRecordReader(InputSplit split,TaskAttemptContext context) ”

如果检查一下源代码,InputFormat抽象类的定义是在InputFormat.java,代码非常短,注释之外只有这两行抽象函数声明。


函数getSplits的功能,是将输入的HDFS文件切分成若干个spit。在Hadoop集群里的每个节点做MapReduce时候处理的时候,每次只处理一个split,所以splitMapReduce处理的最小单元。


函数createRecordReader的功能,是创建RecordReader对象,这个RecordReader对象根据split的内容,将split解析成若干个键值对。在做MapReduce的时候,Mappper会不断地调用RecordReader的功能,从RecordReader里读取键值对,然后用map函数进行处理。


InputFormat类是一个抽象类,它并不具体负责这两个功能的实现。它有3个继承类,DBInputFormat,DelegatingInputFormat类和FileInputFormat类。其中,DBInputFormat类是处理从数据库输入,DelegatingInputFormat类是用在多个输入处理,FileInputFormat类是处理基于文件的输入。


fileInputFormat类为例。FileInputFormat类是一个抽象类,它在InputFormat类的基础上,增加一些跟文件操作相关的函数。它实现了getSplits函数,但没实现createRecordReader函数,它把createRecordReader的实现留给继承类去做。在getSplits函数里,最重要的是这段:






// generatesplits

//这是返回值

List<InputSplit>splits = new ArrayList<InputSplit>();

//获取HDFS文件的信息,FileStatus在前面的章节使用过。

List<FileStatus>files= listStatus(job);

//对作业的每个文件都进行处理

for(FileStatus file: files) {

//获取文件路径

Path path =file.getPath();

FileSystemfs = path.getFileSystem(job.getConfiguration());

//获取文件长度

long length= file.getLen();

//获取文件在HDFS上存储的文件块的位置信息

BlockLocation[]blkLocations = fs.getFileBlockLocations(file, 0, length);


if ((length!= 0) && isSplitable(job, path)) {

//获取文件块的大小

longblockSize = file.getBlockSize();

//根据文件块大小,最小尺寸,最大尺寸,计算出split的大小

longsplitSize = computeSplitSize(blockSize, minSize, maxSize);


//这段代码是根据splitSize,每次计算一个split的块位置和所在主机的位置。

//然后生成split对象存储。

longbytesRemaining = length;

while(((double) bytesRemaining)/splitSize > SPLIT_SLOP) {

intblkIndex = getBlockIndex(blkLocations, length-bytesRemaining);

splits.add(newFileSplit(path, length-bytesRemaining, splitSize,

blkLocations[blkIndex].getHosts()));

bytesRemaining-= splitSize;

}


//最后剩下的不够一个splitSize的数据单独做一个split

if(bytesRemaining != 0) {

splits.add(newFileSplit(path, length-bytesRemaining, bytesRemaining,

blkLocations[blkLocations.length-1].getHosts()));

}

} else if(length != 0) {

//如果文件很小,就直接做成一个split

splits.add(newFileSplit(path, 0, length, blkLocations[0].getHosts()));

} else {

//如果文件尺寸是0,空文件,就创建一个空主机,主要是为了形式上一致。

splits.add(newFileSplit(path, 0, length, new String[0]));

}

}


FileInputFormat5个继承类,包括CombineFileInputFormat类,KeyValueTextInputFormat类,NLineInputFormat类,SequenceFileInputFormat类和TextInputFormat类。这几个有抽象类,也有具体类。


TextInputFormat类为例,它实现了createRecordReader函数,非常简单,函数体只有一个语句,返回一个LineRecordReader


LineRecordReader类继承了抽象类RecordReader。抽象类RecordReader定义的全是抽象函数。LineRecordReader每次从一个InputSplit里读取一行文本,以这行文本在文件中的偏移量为键,以这行文本为值,组成一个键值对,返回给Mapper处理。

2.OutputFormat

OutputFormat类将键值对写入存储结构。一般来说,Mapper类和Reducer类都会用到OutputFormat类。Mapper类用它存储中间结果,Reducer类用它存储最终结果。


OutputFormat是个抽象类,这个类声明了3个抽象函数:

publicabstract RecordWriter<K, V> getRecordWriter(TaskAttemptContext context)

publicabstract void checkOutputSpecs(JobContext context)

publicabstract OutputCommitter getOutputCommitter(TaskAttemptContextcontext)

其中,最主要的函数是getRecordWriter返回RecordWriter,它负责将键值对写入存储部件。函数checkOutputSpecs检查输出参数是否合理,一般是检查输出目录是否存在,如果已经存在就报错。函数getOutputCommitter获取OutputCommitterOutputCommitter类是负责做杂活的,诸如初始化临时文件,作业完成后清理临时目录临时文件,处理作业的临时目录临时文件等等。


OutputFormat4个继承类,有DBOutputFormatFileOutputFormatFilterOutputFormatNullOutputFormat。顾名思义,DBOutputFormat是将键值对写入到数据库,FileOutputFormat将键值对写到文件系统,FilterOutputFormat将其实是提供一种将OutputFormat进行再次封装,类似Java的流的Filter方式,NullOutputFormat将键值对写入/dev/null,相当于舍弃这些值。


FileOutputFormat为例。FileOutputFormat是一个抽象类。它有两个继承类,SequenceFileOutputFormatTextOutputFormatSequenceFileOutputFormat将键值对写入HDFS的顺序文件。TextOutputFormat将数据写入HDFS的文本文件。写入过程类似第3HDFS文件操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值