HDFS的 InputFormat自定义

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;
		}
		@Override
		public void readFields(DataInput in) throws IOException {
			this.url = new URL(in.readUTF());
			//  UTF形式传输
		}

		@Override
		public 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();
	}
	@Override
	public void close() throws IOException {
		lineReader.close();
	}

	@Override
	public Text createKey() {
		return new Text("");
	}

	@Override
	public URLWritable createValue() {
		return new URLWritable();
	}

	@Override
	public long getPos() throws IOException {
		return lineReader.getPos();
	}

	@Override
	public float getProgress() throws IOException {
		return lineReader.getProgress();
	}

	@Override
	public 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>{

	@Override
	public RecordReader<Text, URLWritable> getRecordReader(InputSplit arg0, JobConf arg1, Reporter arg2)
			throws IOException {
		// TODO Auto-generated method stub
		return 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,则实现了这个类。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值