MapReduce技术

一、MapReduce工作原理 - 基本概念

 

 ·yarn操作系统基本组成 Resource Manager(资源调度)、Application Manager(任务调度)、NodeManage(对datanode计算资源)和

container(消息交互的资源描述)。

 ·client(客户端)向yarn提交job(作业),Resource Manager和Application Manager共同启动App Master(一个job对应一个App Master),

MRApp Master是App Master的一种实现。一个MRApp Master和Node manager管理多个Task (Map Task和Reduce Task)。 


import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import package
public class WordCount {
public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, WordCount.class.getSimpleName());
        job.setJarByClass(WordCount.class);

        job.setMapperClass(MyMapper.class);    
        job.setCombinerClass(MyReducer.class); 
        job.setPartitionerClass(MyPartitioner.class);
        job.setReducerClass(MyReducer.class); 
        job.setNumReduceTasks(4);  //分区数为4
       
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);
       
        String uri="hdfs://node:9000/test/WordCount";        
        FileSystem fs = FileSystem.get(URI.create(uri), conf); 
        FileStatus[] inputFiles = fs.listStatus(new Path(uri));
        for (int i = 1; i < inputFiles.length; ++i) 
            FileInputFormat.addInputPath(job, inputFiles[i].getPath());      
        FileOutputFormat.setOutputPath(job, new Path( "hdfs://node:9000/result/WordCount1" ));   //
        if(job.waitForCompletion(true))System.out.println("OK");
    }
    public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
        Text k2 = new Text();
        LongWritable v2 = new LongWritable(1);
        @Override
        protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException {
            // FileSplit fileSplit = (FileSplit) context.getInputSplit();
            // System.out.println(fileSplit.getPath().getName());  //文件名
            String[] split = value.toString().split(" ");
            for (String word : split) {
                k2.set(word);
                context.write(k2, v2);
            }
        }
    }
    public  static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
        LongWritable v3=new LongWritable();
        @Override
        protected void reduce(Text k2, Iterable<LongWritable> v2s,Context context) throws IOException, InterruptedException {
            long sum = 0;
            for (LongWritable one : v2s)
                sum+=one.get(); 
            v3.set(sum); context.write(k2, v3);
        }
    }
 public static class MyPartitioner extends Partitioner<Text, LongWritable> { 
       @Override                                                 
         public int getPartition(Text k, LongWritable v, int numPartitions) {
            ch=k.toString()[0]
            if(Character.isLetter(ch))
              return ((int)Character.toLowerCase(ch)-97)/9;
            else
              return 3;      
        }
    } 
}

 二、MapReduce源数据流

 ·指定IO文件路径:FileInputFormat.addInputPath(JobConf conf, Path path); setInputPaths(JobConf conf, String commaSeparatedPaths) 

 ·分片。把源文件分成一系列的InputSplit,每个inputSplit交由一个Mapper处理。

分片的大小splitSize=max{minSize, min{maxSize, blockSize}}。minSize、maxSize和blockSize默认大小为1MB、10MB和64MB;

blockSize文件块在hdfs-site.xml中设置,因为对网络传输的开销,blockSize比windows下的磁盘块大多了。  

  splitSize是逻辑分片,blockSize是物理分块:对一个大文件设置splitSize > blockSize,Mapper就需要从多个dataNode获取一个

完整的分片;  但是,如果文件实在太大,造就成千上万个分片会给集群带来很大的压力,这时就有必要设置splitSize > blockSize。

TextInputFormat.setMinInputSplitSize(job, 1);
TextInputFormat.setMaxInputSplitSize(job, 1024 * 1024 * 10L);  // 设置为10MB

  ·数据格式化。设置源文件中数据读取格式InputFormat、输出文件的数据格式OutFormat。InputFormat的接口实现有

TextInputformat(默认)、KeyValueTextInputFormat、SequenceFileInputFormat、NLineInputFormat、CombineFileInputFormat

   对于TextInputFormat把输入文本逐行送入mapper函数处理,其中该行的起始偏移量作为Key,该行的内容为Value。

  对于KeyValueTextInputFormat  默认以\t分隔key, value,在Mapper函数中设置k v类型。使用:参考示例

import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat
conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", "#");  // 以#分隔
job.setInputFormatClass(KeyValueTextInputFormat.class);

   对于NLineInputFormat  使用:   参考示例   

NLineInputFormat.setNumLinesPerSplit(job, 4);     //每4行一个split
job.setMapperClass(NLineInputFormatMapper.class);
job.setReducerClass(NLineInputFormatReducer.class);

 三、Mapreduce过程 (参数调优)

 

  ·mapper把接受的<key,value>处理成<k,v>,mapper端的Shuffle过程把<k,v>进一步优化成reducer的直接输入数据。

 ·每一个mapper任务在内存中有一个输出缓存(默认100MB)和一个写入阈值(默认0.8)。

依据k值进行sort、combine和write(spill操作写入磁盘),结果生成多个spill文件,内部有序且合并为<k, v.list>。

 ·reduce的shuffle端用http协议,把spill从硬盘复制到JVM堆内存中,默认http线程数为5、内存阈值为0.7;

对每个spill通过归并排序把它变成全局有序,并依据k值存放于若干partition中。

 ·reducer把接受的<k,  v.list>处理成新的<k,v>,OutputForamt输出[key \t value]。

  因为中间有磁盘IO操作,有必要做数据压缩;设置sort比较策略、分区策略、分区大小: 

job.setSortComparatorClass(DefineCompparator.class);          //设置自定义的比较策略
job.setPartitionerClass(DefinedPartition.class);
job.setNumReduceTasks(1);
//job.setGroupingComparatorClass(DefinedGroupSort.class);       //设置自定义的分组策略    
job.getConfiguration().setBoolean("mapred.output.compress", true);       // 设置自定义的压缩格式 Bzip2Codec.class
job.getConfiguration().setClass("mapred.output.compression.codec", Bzip2Codec.class, CompressionCodec.class); 

 四、Hadoop Streaming

  Hadoop Streaming使java外的其他语言实现MapReduce。如shell脚本

#! /bin/sh
# mapper.sh
while read LINE; do
        for word in $LINE
        do
                echo "$word 1"
        done
done
#! /bin/sh
# reducer.sh
count=0;started=0;word=""
while read LINE;do
  newword=`echo $LINE | cut -d ' '  -f 1`
  if [ "$word" != "$newword" ];then
    [ $started -ne 0 ] && echo -e "$word\t$count"
    word=$newword
    count=1
    started=1
  else
    count=$(( $count + 1 ))
  fi
done
echo -e "$word\t$count"
mapper.sh和reducer.sh

测试:   

 

  2020-06-10 08:34:19    

posted @ 2021-12-09 10:02  shines87  阅读(102)  评论(0)    收藏  举报