关于MapReduce中自定义分区类(四)

MapTask类

在MapTask类中找到run函数

  1. if(useNewApi){
  2.       runNewMapper(job, splitMetaInfo, umbilical, reporter);
  3.     }

再找到runNewMapper

  1. @SuppressWarnings("unchecked")
  2.   private<INKEY,INVALUE,OUTKEY,OUTVALUE>
  3.   void runNewMapper(final JobConf job,
  4.                     final TaskSplitIndex splitIndex,
  5.                     final TaskUmbilicalProtocol umbilical,
  6.                     TaskReporter reporter
  7.                     ) throws IOException,ClassNotFoundException,
  8.                              InterruptedException{
  9.     // make a task context so we can get the classes
  10.     org.apache.hadoop.mapreduce.TaskAttemptContext taskContext =
  11.       new org.apache.hadoop.mapreduce.task.TaskAttemptContextImpl(job,
  12.                                                                   getTaskID(),
  13.                                                                   reporter);
  14.     // make a mapper
  15.     org.apache.hadoop.mapreduce.Mapper<INKEY,INVALUE,OUTKEY,OUTVALUE> mapper =
  16.       (org.apache.hadoop.mapreduce.Mapper<INKEY,INVALUE,OUTKEY,OUTVALUE>)
  17.         ReflectionUtils.newInstance(taskContext.getMapperClass(), job);
  18.     // make the input format
  19.     org.apache.hadoop.mapreduce.InputFormat<INKEY,INVALUE> inputFormat =
  20.       (org.apache.hadoop.mapreduce.InputFormat<INKEY,INVALUE>)
  21.         ReflectionUtils.newInstance(taskContext.getInputFormatClass(), job);
  22.     // rebuild the input split
  23.     org.apache.hadoop.mapreduce.InputSplit split = null;
  24.     split = getSplitDetails(newPath(splitIndex.getSplitLocation()),
  25.         splitIndex.getStartOffset());
  26.     LOG.info("Processing split: "+ split);
  27.  
  28.     org.apache.hadoop.mapreduce.RecordReader<INKEY,INVALUE> input =
  29.       newNewTrackingRecordReader<INKEY,INVALUE>
  30.         (split, inputFormat, reporter, taskContext);
  31.  
  32.     job.setBoolean(JobContext.SKIP_RECORDS, isSkipping());
  33.     org.apache.hadoop.mapreduce.RecordWriter output = null;
  34.  
  35.     // get an output object
  36.     if(job.getNumReduceTasks()==0){
  37.       output =  如果jreduce个数等于0.则执行该方法
  38.         newNewDirectOutputCollector(taskContext, job, umbilical, reporter);
  39.     }else{
  40.        如果reduce个数大于0.则执行该方法
  41.       output =newNewOutputCollector(taskContext, job, umbilical, reporter);
  42.     }
  43.  
  44.     org.apache.hadoop.mapreduce.MapContext<INKEY, INVALUE, OUTKEY, OUTVALUE>
  45.     mapContext =
  46.       newMapContextImpl<INKEY, INVALUE, OUTKEY, OUTVALUE>(job, getTaskID(),
  47.           input, output,
  48.           committer,
  49.           reporter, split);
  50.  
  51.     org.apache.hadoop.mapreduce.Mapper<INKEY,INVALUE,OUTKEY,OUTVALUE>.Context
  52.         mapperContext =
  53.           newWrappedMapper<INKEY, INVALUE, OUTKEY, OUTVALUE>().getMapContext(
  54.               mapContext);
  55.  
  56.     try{
  57.       input.initialize(split, mapperContext);
  58.       mapper.run(mapperContext);
  59.       mapPhase.complete();
  60.       setPhase(TaskStatus.Phase.SORT);
  61.       statusUpdate(umbilical);
  62.       input.close();
  63.       input = null;
  64.       output.close(mapperContext);
  65.       output = null;
  66.     } finally {
  67.       closeQuietly(input);
  68.       closeQuietly(output, mapperContext);
  69.     }
  70.   }

我们知道,分区是在map函数输出的时候做的 ,所以这里是get output object

  1. // get an output object
  2.     if(job.getNumReduceTasks()==0){
  3.  
  4.       output =  如果jreduce个数等于0.则执行该方法
  5.         newNewDirectOutputCollector(taskContext, job, umbilical, reporter);
  6.     }else{
  7.        如果reduce个数大于0.则执行该方法
  8.       output =newNewOutputCollector(taskContext, job, umbilical, reporter);
  9.     }

如果没有reduce任务,则new NewDirectOutputCollector()

(Collection过程我还没探索过呢)

如果有NewOutputCollector任务,则运行new NewOutputCollector()

内部类NewOutputCollector

在内部类NewOutputCollector中找到该方法(构造方法)

  1. NewOutputCollector(org.apache.hadoop.mapreduce.JobContext jobContext,
  2.                        JobConf job,
  3.                        TaskUmbilicalProtocol umbilical,
  4.                        TaskReporter reporter
  5.                        ) throws IOException,ClassNotFoundException{
  6.       collector = createSortingCollector(job, reporter);
  7.  
  8.       partitions = jobContext.getNumReduceTasks();
  9.  
  10.       if(partitions >1){
  11.         partitioner =(org.apache.hadoop.mapreduce.Partitioner<K,V>)
  12.           ReflectionUtils.newInstance(jobContext.getPartitionerClass(), job);
  13.       }else{
  14.         partitioner =new org.apache.hadoop.mapreduce.Partitioner<K,V>(){
  15.           @Override
  16.           publicint getPartition(K key, V value,int numPartitions){
  17.             return partitions -1;
  18.           }
  19.         };
  20.       }
  21.     }

通过partitions = jobContext.getNumReduceTasks();语句获取到Reduce任务个数

如果Reduce任务数小于等于1,则新建一个Partitioner对象的同时并复写getPartition方法,这个复写的方法直接统一返回-1,就都在一个分区了。

如果Reduce任务数大于 ,则通过反射创建jobContext.getPartitionerClass()获取到的对象

于是查看:

jobContext接口

jobContext接口中的

  1. /**
  2.    * Get the {@link Partitioner} class for the job.
  3.    *
  4.    * @return the {@link Partitioner} class for the job.
  5.    */
  6.   publicClass<? extends Partitioner<?,?>> getPartitionerClass()
  7.      throws ClassNotFoundException;

我们还是看其实现类jobContextImpl吧

jobContextImpl类

注意是在mapreduce包下啊,不是mapred包下

  1. /**
  2.    * Get the {@link Partitioner} class for the job.
  3.    *
  4.    * @return the {@link Partitioner} class for the job.
  5.    */
  6.   @SuppressWarnings("unchecked")
  7.   publicClass<? extends Partitioner<?,?>> getPartitionerClass()
  8.      throws ClassNotFoundException{
  9.     return(Class<? extends Partitioner<?,?>>)
  10.       conf.getClass(PARTITIONER_CLASS_ATTR,HashPartitioner.class);
  11.   }

conf.getClass(PARTITIONER_CLASS_ATTR, HashPartitioner.class);

的意思是,从PARTITIONER_CLASS_ATTR属性中取出值,作为类返回,如果不存在,则使用和默认值HashPartitioner.class

也就是说,当Reduce个数大于1的时候,其默认调用的是HashPartitioner.class

  1. publicclassHashPartitioner<K, V>extendsPartitioner<K, V>{
  2. /** Use {@link Object#hashCode()} to partition. */
  3. publicint getPartition(K key, V value,
  4. int numReduceTasks){
  5. return(key.hashCode()&Integer.MAX_VALUE)% numReduceTasks;
  6. }
  7. }

发现HashPartitioner调用的是getPartition方法,最终使用的是key对象中的hashcode方法

而我们使用eclipse(Alt+Shift+ S  按下H)复写的hashcode是将两个属性(账户和金额都考虑进去了)

嗯,果然自己修改自定义key类中的hashcode,测试了一下是可以的,只要hashcode是只根据我们的账户account进行生产

  1. @Override
  2.         publicint hashCode(){
  3.             final int prime =31;
  4.             int result =1;
  5.             result = prime * result +((account == null)?0: account.hashCode());
  6.      //     result = prime * result + ((amount == null) ? 0 : amount.hashCode());
  7.             return result;
  8.         }

另一种更主流的方式:

自定义的Partition类为什么要是Group的内部类呢?自己改为外部类自己测试下,发现完全可以

具体的形式

  1. publicstaticclassKeyPartitioner extends  Partitioner<SelfKey,DoubleWritable>{
  2.  
  3.             @Override
  4.             publicint getPartition(SelfKey key,DoubleWritable value,int numPartitions){
  5.                 /**
  6.                  * 如何保证数据整体输出上的有序,需要我们自定义业务逻辑
  7.                  * 必须提示前知道num reduce task 个数?
  8.                  * \w  单词字符[a-zA-Z_0-9]
  9.                  *  
  10.                  */
  11.                 String account =key.getAccount();
  12.                 //0xxaaabbb 0-9 
  13.                 //[0-2][3-6][7-9]
  14.                 if(account.matches("\\w*[0-2]")){
  15.                     return0;
  16.                 }elseif(account.matches("\\w*[3-6]")){
  17.                     return1;
  18.                 }elseif(account.matches("\\w*[7-9]")){
  19.                     return2;
  20.                 }
  21.                 return0;
  22.  
  23.             }
  24.         }

这是为了保证S1和S2都在分区1,而不会出现S1中的其中几个在分区1 ,另外几个在分区2

因为我们此时的键——是账户+金额,所以可能明明都是账户S1的分区却不一样,最后导致排序混乱?

来自为知笔记(Wiz)

时间: 2024-08-08 11:02:46

关于MapReduce中自定义分区类(四)的相关文章

关于MapReduce中自定义分组类(三)

Job类  /**    * Define the comparator that controls which keys are grouped together    * for a single call to    * {@link Reducer#reduce(Object, Iterable,    *                       org.apache.hadoop.mapreduce.Reducer.Context)}    * @param cls the raw

Mapreduce中自定义分区

Reducer任务的数据来自于Mapper任务,也就说Mapper任务要划分数据,对于不同的数据分配给不同的Reducer任务运行.Mapper任务划分数据的过程就称作Partition.负责实现划分数据的类称作Partitioner. 默认的分区类是HashPartitioner,是处理Mapper任务输出的,getPartition()方法有三个形参,key.value分别指的是Mapper任务的输出,numReduceTasks指的是设置的Reducer任务数量,默认值是1.那么任何整数与

关于MapReduce中自定义Combine类(一)

MRJobConfig public static fina COMBINE_CLASS_ATTR 属性COMBINE_CLASS_ATTR = "mapreduce.job.combine.class" ————子接口(F4) JobContent 方法getCombinerClass ————子实现类 JobContextImpl 实现getCombinerClass方法: public Class<? extends Reducer<?,?,?,?>> g

在hadoop作业中自定义分区和归约

当遇到有特殊的业务需求时,需要对hadoop的作业进行分区处理 那么我们可以通过自定义的分区类来实现 还是通过单词计数的例子,JMapper和JReducer的代码不变,只是在JSubmit中改变了设置默认分区的代码,见代码: //1.3分区 //设置自定义分区类 job.setPartitionerClass(JPartitioner.class); //设置分区个数--这里设置成2,代表输出分为2个区,由两个reducer输出 job.setNumReduceTasks(2); 自定义的JP

hadoop编程小技巧(3)---自定义分区类Partitioner

Hadoop代码测试环境:Hadoop2.4 原理:在Hadoop的MapReduce过程中,Mapper读取处理完成数据后,会把数据发送到Partitioner,由Partitioner来决定每条记录应该送往哪个reducer节点,默认使用的是HashPartitioner,其核心代码如下: /** Use {@link Object#hashCode()} to partition. */ public int getPartition(K2 key, V2 value, int numRe

读取SequenceFile中自定义Writable类型值

1)hadoop允许程序员创建自定义的数据类型,如果是key则必须要继承WritableComparable,因为key要参与排序,而value只需要继承Writable就可以了.以下定义一个DoubleArrayWritable,继承自ArrayWritable.代码如下: 1 package matrix; 2 import org.apache.hadoop.io.*; 3 public class DoubleArrayWritable extends ArrayWritable { 4

GreenPlum中自定义分区维护函数

GreenPlum中的分区表在数据量较大的情况下对于提升查询性能的帮助非常的,但是GreenPlum本身并没有提供分区表自动维护的工具,这里我们利用GreenPlum的PL/SQL自定义两个分区表自动维护的存储过程(也可以成为函数). 创建存储过程之前首先要创建一个记录分区表详细信息的视图,这里可以参见上篇博文.由于业务中有多张表需要做分区,而且分区字段的类型并不一样,因此我们首先创建一张字典表,记录每张表的分区类型,如下: CREATE TABLE op_tb_partition (   tb

Hadoop Mapreduce 中的FileInputFormat类的文件切分算法和host选择算法

文件切分算法 文件切分算法主要用于确定InputSplit的个数以及每个InputSplit对应的数据段. FileInputFormat以文件为单位切分成InputSplit.对于每个文件,由以下三个属性值确定其对应的InputSplit的个数. goalSize:根据用户期望的InputSplit数据计算,即totalSize/numSplit.totalSize为文件总大小:numSplit为用户设定的Map Task个数,默认情况下是1. minSize:InputSplit的最小值,由

c#(winform)中自定义ListItem类方便ComboBox添加Item项

1.定义ListItem类 public class ListItem { private string _key = string.Empty; private string _value = string.Empty; public ListItem(string pKey, string pValue) { _key = pKey; _value = pValue; } public override string ToString() { return this._value; } pu