• 8.2.1输入分片InputSplit和输入处理格式FileInputFormat


    1.1.1         输入分片和记录

    1)输入分片InputSplit接口

    输入分片一般是文件,也可以数据库中的若干行。记录对应一行数据。输入分片在java表示为InputSplit接口,getlength函数返回大小,用于分片排序,大的先处理。Getlocation函数返回分片位置,让map任务尽量本地化。分片并不包含数据本身,而是指向数据的索引。

    public abstract class InputSplit {

      /**

       * Get the size of the split, so that the input splits can be sorted by size.

       * @return the number of bytes in the split

       * @throws IOException

       * @throws InterruptedException

       * split的长度用byte表示

       */

      public abstract long getLength() throws IOException, InterruptedException;

     

      /**

       * Get the list of nodes by name where the data for the split would be local.

       * The locations do not need to be serialized.

       * 获取split所在的节点

       * @return a new array of the node nodes.

       * @throws IOException

       * @throws InterruptedException

       */

      public abstract

        String[] getLocations() throws IOException, InterruptedException;

     

      /**

       * Gets info about which nodes the input split is stored on and how it is

       * stored at each location.

       * 返回split所在的节点信息以及在该节点上如何存储 memory

       * @return list of <code>SplitLocationInfo</code>s describing how the split

       *    data is stored at each location. A null value indicates that all the

       *    locations have the data stored on disk.

       * @throws IOException

       */

      @Evolving

      public SplitLocationInfo[] getLocationInfo() throws IOException {

        return null;

      }

    }

    2InputFormat输入处理类

    应用开发人员不直接处理InputSplit,而是InputFormat创建分片并将分片分割为记录。所有InputFormat都要直接或间接的继承InputFormat抽象类。包含分片切割函数getSplits()和创建读取记录操作对象的函数createRecordReader函数。

    getSplits()方法

    此方法接受JobContext接受环境信息,得到要处理的文件信息后,进行逻辑切割,产生InputSplit集合返回。然后将分片发送给application master,AM根据分片位置创建map任务。

    List<InputSplit> getSplits(JobContext context) throws IOException, InterruptedException;

    createRecordReader函数

    根据传入的分片,创建RecordReader对象,RecordReader相当于迭代器,取出记录生成键值对,传给Mapper的 run方法。Run方法中通过context.nextKeyValue()切换迭代器指向的键值。

    public abstract RecordReader<K,V> createRecordReader(InputSplit split,TaskAttemptContext context) throws IOException, InterruptedException;

    3FileInputFormat

    继承于InputFormat,以文件作为数据的源的基类,作用是指出输入文件的位置和实现输入分片的代码,把分片分割成记录是FileInputFormat子类的完成,子类包括CombineFileInputFormat、TextInputFormat、KeyValue TextInputFormat、NLineInputFormat等

    4FileInputFormat类的输入路径

    输入路径:Add方法添加路径,set方法设置路径。一条路径可以是一个文件、一个目录、或二者的集合。默认不递归子目录,需要将mapreduce.input.fileinputformat.input.dir.recuresive设置为true才会递归子目录。public static void addInputPath(Job job,Path path)

    public static void addInputPaths(Job job,String commaseparatePaths)

    public static void setInputPaths(Job job,Path… inputPaths)

    public static void setInputPaths(Job job,String commaseparatePaths)

    过滤器:还可以用FileInputFormat的setInputPathFilter()方法设置过滤器来过滤非隐藏的文件,默认过滤排除隐藏文件(名称中以“.”和“_”开头文件)。

    属性设置路径和过滤器:路径和过滤器可以用属性来设置,String任务可以通过-input选项设置路径

    5FileInputFormat类的输入分片

    FileInputFormat会对超过HDFS块的输入进行分片,默认分片大小等于块大小,也可以通过属性设置,分片大小计算:max(minimumSize,min(maximumSize, blockSize))。可以通过调节maximumSize的大小来调节分片大小。

    6)小文件与CombineFileInputFormat

    Hadoop适合处理大文件,对于大量的小文件,如果每个文件创建一个map任务,会导致map任务开销太大,增加小文件的寻址次数,尽量避免大量小文件处理,所以用CombineInputFormat把多个小文件打包到一个分片,并且考虑多个文件的机架节点关系。

    7)避免切分

    如果不希望文件被切分,例如判断文件中记录是否有序,可以让minimumSize值大于最大文件的大小,但是文件的大小不能超过blockSize,或者重写FileInputFormat方法isSplitable()返回为false。下面介绍将多个小文件合成一个大的序列文件的例子:

    1)自定义完整文件输入处理类如下:

    Public class WholeFileInputFormat extends FileInputFormat<NullWritable, ByteWritable>

    {

    @override//不得分片

    protected boolean isSplitable(JobContext context,Path file){return false;}

           @override

           public RecordReader<NullWritable,BytesWritable> createRecordReader ( InputSplit split,TaskAttemptContext context )throws IOException,InterruptedException

    {

      WholeFileRecordReader reader=new WholeFileRecordReader();

      reader.initialize(split,context);

      return reader;

    }

    }

    2)自定义完整文件读取类WholeFileRecordReader

    WholeFileRecordReader类通过initialize()方法传入文件信息,然后调用nextKeyValue()方法一次性读取整个文件的内容,通过布尔值processed判断是否读取执行过。其他函数都是返回值。将FileSplit转为一条记录,键为null,值为文件内容。

    package org.edu.bupt.xiaoye.hadooptest;

    import java.io.IOException;

    import org.apache.hadoop.conf.Configuration;

    import org.apache.hadoop.fs.FSDataInputStream;

    import org.apache.hadoop.fs.FileSystem;

    import org.apache.hadoop.fs.Path;

    import org.apache.hadoop.io.BytesWritable;

    import org.apache.hadoop.io.IOUtils;

    import org.apache.hadoop.io.NullWritable;

    import org.apache.hadoop.mapreduce.InputSplit;

    import org.apache.hadoop.mapreduce.RecordReader;

    import org.apache.hadoop.mapreduce.TaskAttemptContext;

    import org.apache.hadoop.mapreduce.lib.input.FileSplit;

    /**

     * 继承RecordReader

     * 该类用来将分片分割成记录,从而生成key和value。例如TextInputFormat中的key和value就是RecordReader的子类产生的。

     * 在这里,我们继承了这个类,将重写了key和value的生成算法。对一个分片来说,只生成一个key-value对。其中key为空,value为该分片

     * 的所有内容

     * @author Xiaoye

     */

    public class WholeFileRecordReader extends

                  RecordReader<NullWritable, BytesWritable> {

           // 用来盛放传递过来的分片

           private FileSplit fileSplit;

           private Configuration conf;

           //将作为key-value中的value值返回

           private BytesWritable value = new BytesWritable();

           // 因为只生成一条记录,所以只需要调用一次。因此第一次调用过后将processed赋值为true,从而结束key和value的生成

           private boolean processed = false;

           /**

            * 设置RecordReader的分片和配置对象。

            */

           @Override

           public void initialize(InputSplit split, TaskAttemptContext context)

                         throws IOException, InterruptedException {

                  this.fileSplit = (FileSplit) split;

                  this.conf = context.getConfiguration();

           }

           /**

            * 核心算法

            * 用来产生key-value值

            * 一次读取整个文件内容注入value对象

            */

           @Override

           public boolean nextKeyValue() throws IOException, InterruptedException {

                  if (!processed) {

                         /*

                          * 注意这儿,fileSplit中只是存放着待处理内容的位置 大小等信息,并没有实际的内容

                          * 因此这里会通过fileSplit找到待处理文件,然后再读入内容到value中

                          */

                         byte[] contents = new byte[(int) fileSplit.getLength()];

                         Path file = fileSplit.getPath();

                         FileSystem fs = file.getFileSystem(conf);

                         FSDataInputStream in = null;

                         try {

                                in = fs.open(file);

                                IOUtils.readFully(in, contents, 0, contents.length);

                                value.set(contents, 0, contents.length);

                         } finally {

                                IOUtils.closeStream(in);

                         }

                         processed = true;

                         return true;

                  }

                  return false;

           }

           @Override

           public NullWritable getCurrentKey() throws IOException,

                         InterruptedException {

                  return NullWritable.get();

           }

           @Override

           public BytesWritable getCurrentValue() throws IOException,

                         InterruptedException {

                  return value;

           }

           @Override

           public float getProgress() throws IOException, InterruptedException {

                  return processed ? 1.0f : 0.0f;

           }

           @Override

           public void close() throws IOException {

                  //do nothing

           }

    3)将若干个小文件打包成顺序文件的mapreduce作业

    通过WholeFileRecordReader类读取所有小文件的内容,以文件名称为输出键,以内容未一条记录,然后合并成一个大的顺序文件。

    public class SmallFilesToSequenceFileConverter extends configured implement Tool

    {

           package com.pzoom.mr.sequence;

    import java.io.IOException;

    import java.util.Random;

    import org.apache.hadoop.conf.Configuration;

    import org.apache.hadoop.fs.Path;

    import org.apache.hadoop.io.BytesWritable;

    import org.apache.hadoop.io.NullWritable;

    import org.apache.hadoop.io.Text;

    import org.apache.hadoop.mapreduce.InputSplit;

    import org.apache.hadoop.mapreduce.Job;

    import org.apache.hadoop.mapreduce.Mapper;

    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 org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;

    public class SmallFilesToSequenceFileConverter {

    ///定义map函数

           static class SequenceFileMapper extends Mapper<NullWritable, BytesWritable, Text, BytesWritable> {

                  private Text filenameKey;

                  //定义设置文件函数

                  @Override

                  protected void setup(Context context) throws IOException,

                  InterruptedException {

                         InputSplit split = context.getInputSplit();

                         Path path = ((FileSplit)split).getPath();

                         filenameKey = new Text(path.toString());

                  }

     //定义map函数

                  @Override

                  protected void map(NullWritable key, BytesWritable value,

                                Context context) throws IOException, InterruptedException {

                         context.write(filenameKey, value);

                  }

    //定义run函数

    @Override

    public int run (String[] args)throws IOException {

           Configuration conf = getConf();

           if(conf==null)

    {

           return -1;

    }

           Job job=JobBuilder.parseInputAndOutput(this,conf,args);

                  job.setInputFormatClass(WholeFileInputFormat.class);

                  job.setOutputFormatClass(SequenceFileOutputFormat.class);输出序列file

                  job.setOutputKeyClass(Text.class);

                  job.setOutputValueClass(BytesWritable.class);

                  job.setMapperClass(SequenceFileMapper.class);

                 

                  return job.waitForCompletion(true)? 0:1;}

    //args传入输入输出路径

     public static void main(String[] args) throws IOException{

                  int exitCode=ToolRunner.run(new SmallFilesToSequenceFileConverter(),args);

    System.exit(exitCode);

                         }

    }

    }

    4)执行小文件合并为大文件的命令

    各参数含义:采用本地配置文件,两个reduces任务,输入文件夹,输出文件夹

    %hadoop jar job.jar SmallFilesToSequenceFileConverter –conf conf/Hadoop-localhost.xml –D mapreduece.job.reduces-2 input/smallfiles output

    5)通过命令来查看输出结果

    %hadoop fs –conf conf/Hadoop-localhost.xml –text output/part-r-00000

     

    输出结果是以小文件路径为键,以内容为值的合并序列文件

    8)分片中的文件信息

    可以调用Mapper的Context对象的getInputSplit()方法获取InputSplit对象,强制转为FileSplit,可以从对象中获取分片所属文件的信息。

     

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

  • 相关阅读:
    [转载]qemu-kvm安装配置
    Hadoop通过c语言API访问hdfs
    hadoop和hdfs环境搭建
    OpenCV installation for Ubuntu 12.04
    homework-01
    linux命令2
    压缩tar
    anaconda 安装opencv
    anconda安装第三方库
    开源代码
  • 原文地址:https://www.cnblogs.com/bclshuai/p/12254837.html
Copyright © 2020-2023  润新知