HDFS的 InputFormat自定义

来源:互联网 发布:聊天软件开发 编辑:程序博客网 时间:2024/05/19 07:42

HDFS:

比如一个100TB的文件装入HDFS集群。

并行处理切分文件就非常重要。因为每台机器都保存着大文件的一部分,则从文件中间开始处理文件就很重要

Hadoop的文件系统提供了FSDataInputStream类,而未使用DataInputStream类,主要因为FSD实现了文件的随机读写功能

这样每个分片都由它所驻留的机器进行处理,就自动实现了并行。

流程为 文件(100TB)   -->   分片( Input Split ) (小于HDFS的块大小)(逻辑划分,不同于HDFS块是物理划分)  -->   记录 (一对键和值)  -->   键值对

如果一个记录跨越了一个节点,它会通过网络从另一个节点获取记录的剩余片段。

所以所有大于块大小的文件都需要分片。

每个map任务分配一个分片。 每个map( ) 输入一对键值。

InputFormat接口: 

Hadoop 分割与读取输入文件的方式被定义在InputFormat接口中。

默认实现为TextInputFormat , 这种方式 一行为一个记录,键值对为 每行的字节偏移量 和 该行的Text 。


getSplits( ) 确定输入文件,并将之分片。

类FileInputFormat实现InputFormat接口,并实现了getSplits( ) , 把输入数据分为一组分片,分片数目在numSplits 中限定。

FIF类还有isSplitable( ) 方法,规定此文件能否分片。(比如一些压缩文件不能分片)

getRecordReader( )  负责把输入分片解析为记录,再把记录解析为键值对。

import java.io.DataInput;import java.io.DataOutput;import java.io.IOException;import java.net.MalformedURLException;import java.net.URL;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapred.FileInputFormat;import org.apache.hadoop.mapred.FileSplit;import org.apache.hadoop.mapred.InputSplit;import org.apache.hadoop.mapred.JobClient;import org.apache.hadoop.mapred.JobConf;import org.apache.hadoop.mapred.KeyValueLineRecordReader;import org.apache.hadoop.mapred.MapReduceBase;import org.apache.hadoop.mapred.RecordReader;import org.apache.hadoop.mapred.Reporter;import org.apache.hadoop.mapred.FileOutputFormat;import org.apache.hadoop.io.Writable;class URLWritable implements  Writable{protected URL url; public URLWritable(){}public URLWritable(URL a){this.url = a;}@Overridepublic void readFields(DataInput in) throws IOException {this.url = new URL(in.readUTF());//  UTF形式传输}@Overridepublic void write(DataOutput out) throws IOException {out.writeUTF(url.toString());}public void set (String s) throws MalformedURLException{url = new URL(s);}}class UrlLineRecordReader implements RecordReader<Text, URLWritable>{private KeyValueLineRecordReader lineReader;// 基于KeyValueLineRecordReader 类的包装private Text linekey, linevalue;public UrlLineRecordReader(JobConf job,FileSplit split) throws IOException{lineReader = new KeyValueLineRecordReader(job, split);linekey = lineReader.createKey();linevalue = lineReader.createValue();}@Overridepublic void close() throws IOException {lineReader.close();}@Overridepublic Text createKey() {return new Text("");}@Overridepublic URLWritable createValue() {return new URLWritable();}@Overridepublic long getPos() throws IOException {return lineReader.getPos();}@Overridepublic float getProgress() throws IOException {return lineReader.getProgress();}@Overridepublic boolean next(Text arg0, URLWritable arg1) throws IOException {if(!lineReader.next(linekey, linevalue)){return false;}arg0.set(linekey);arg1.set(linevalue.toString());return true;}}class UrlTextInputFormat extends  FileInputFormat<Text, URLWritable>{@Overridepublic RecordReader<Text, URLWritable> getRecordReader(InputSplit arg0, JobConf arg1, Reporter arg2)throws IOException {// TODO Auto-generated method stubreturn new UrlLineRecordReader(arg1,(FileSplit)arg0);}}

整体思路为 现在要重写InputFormat,来自定义分割文件,解析成键值对的过程。

重写InputFotmat需要重写两个函数。 getSplits( )函数来改变分片格式,getRecordReader( ) 决定生成键值对的过程。

FIleInputFormat 实现了getSplits( ) ,  则如果只想简单改变键值,就可继承自FileInputFormat。 自己只需实现getRecordReader( ).

getRecordReader( ) 返回值为 RecordReader< K,V> 类型,RR< K,V > 类型有6个函数需要实现。

这里通过在KeyValueLineRecordReader基础上包装实现了自己的RR<K,V>类。 KVLRR是Hadoop实现的KeyValueTextInputFornat类中的RecordReader实现。

这里实现自己的RecordReader类时需要URLWritable,则实现了这个类。


0 0