1、介绍
在经过split阶段后,将会在RecordReader的类或者其子类中将split(分片的数据)读取成<key,value>键值对,这样就可以讲split分片中的数据以键值对的方式读入到map端去了。
2、详解
首先我们看一下org.apache.hadoop.mapreduce.Mapper类中的结构
<span style="font-size:18px;">public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> { public abstract class Context implements MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> { } protected void setup(Context context ) throws IOException, InterruptedException { // NOTHING } @SuppressWarnings("unchecked") protected void map(KEYIN key, VALUEIN value, Context context) throws IOException, InterruptedException { context.write((KEYOUT) key, (VALUEOUT) value); } protected void cleanup(Context context ) throws IOException, InterruptedException { // NOTHING } public void run(Context context) throws IOException, InterruptedException { setup(context); try { while (context.nextKeyValue()) { map(context.getCurrentKey(), context.getCurrentValue(), context); } } finally { cleanup(context); } } }</span>
A、从Mapper类中的run()方法看来,Mapper任务过程中,限制性setup(),方法,在setup()方法中可以在map()函数之前进行一些预备的工作,例如简历布隆过滤器、读取分布式缓存等操作。
B、执行完setup()函数以后将会执行context.nextKeyValue()方法。而在一个任务被执行后,context的实际对象是MapContextImpl对象,下面我们看一下这个类中的方法:
<span style="font-size:18px;">public class MapContextImpl<KEYIN,VALUEIN,KEYOUT,VALUEOUT> extends TaskInputOutputContextImpl<KEYIN,VALUEIN,KEYOUT,VALUEOUT> implements MapContext<KEYIN, VALUEIN, KEYOUT, VALUEOUT> { private RecordReader<KEYIN,VALUEIN> reader; private InputSplit split; public MapContextImpl(Configuration conf, TaskAttemptID taskid, RecordReader<KEYIN,VALUEIN> reader, RecordWriter<KEYOUT,VALUEOUT> writer, OutputCommitter committer, StatusReporter reporter, InputSplit split) { super(conf, taskid, writer, committer, reporter); this.reader = reader; this.split = split; } /** * Get the input split for this map. */ public InputSplit getInputSplit() { return split; } @Override public KEYIN getCurrentKey() throws IOException, InterruptedException { return reader.getCurrentKey(); } @Override public VALUEIN getCurrentValue() throws IOException, InterruptedException { return reader.getCurrentValue(); } @Override public boolean nextKeyValue() throws IOException, InterruptedException { return reader.nextKeyValue(); } }</span>
我们从MapContextImpl类中可以看出,在该示例对象调用nextKeyValue()方法就是通过封装的方式执行RecordReader类中的nextKeyValue()方法,这样就从RecordReader类或者其子类中获取了Split分片<key,value>的数据。然后使用同样的封装方式调用RecordReader方法中的getCurrentKey和getCurrentValue方法获取相应的key值和value值,接着执行相应的map函数
C、map()方法使我们实现MapReduce功能需要进行自定义的主要方法,通过重写map()方法,我们让map()函数实现相应的业务逻辑。并且我们从run()方法中可以看出,每当生成一个KeyValue键值对时就会执行一次map()方法,对产生的keyvalue键值对进行相应的处理。
D、最后执行的cleanup()函数。因为cleanup()方法时在finally结构体中,所以其必然会执行,这是我们可以通过cleanup()方法去关闭一些全局资源,例如DataInputStream类等。同样我们可以讲较少的结果存储在一个静态数据中,在cleanup()方法中调用context.write()方法,将map的结果写入到上下文对象中,例如求出topn案例等。
E、当run、setup、map、cleanup方法都执行完成后,经过处理里所形成新的<key,value>键值对将通过context.write()方法写入到上下文中。
3、总结
Map端是自定义进行处理<key,value>键值对的,通过不同的设计模式,可以针对不同的数据和业务需求进行相应的处理,从而实现客户的需求。