Hadoop实践(三)---MR作业运行(源码详解)

来源:互联网 发布:中国广电网络全国整合 编辑:程序博客网 时间:2024/06/06 13:02

要运行MapReduce作业首先要编写MR程序。
MR程序主要包括三个部分:Map类,Reduce类和驱动类。

定义一个反向索引的例子,首先需要定义map类,reduce类需要将输入数据中的每个单词单独显示一行,那么map 的输出key应该是输入文件中的每个单词,然后MR将这些key合并到一起,每个key 的值将包含文件名

map类代码:

 public static class Map extends Mapper<LongWritable, Text, Text, Text> {        //在扩展MR的mapper类时,需要确定输入和和输出的键值的数据类型。在作业中使用MR默认的InputFormat,该类将输入文件中的字节偏移量作为键,将每一行的数据作为值        private Text documentId;  //输入以文本对象的形式存储文件名        private Text word = new Text();  //为了减少对象的创建,需要创建一个可以复用的单一的文本对象        @override        protected void setup(Context context){  //上下文中提取文件名        //setup方法在map方法调用之前执行,使用这个机会存储map中的输入文件名            String filename = ((FileSplit)context.getInputSplit()).getPath().getName();            documentId = new Text(filename);            }        public void map(LongWritable key, Text value, Context context        ) throws IOException, InterruptedException {//每行输入都要调用map方法,map任务同步处理输入文件的子集         for(String token: StringUtils.split(Value.toString())){  //value包括文件的一整行,使用StringUtils(比String.split效率高)标记每行的值             word.set(token);             context.write(word,documentId);  //map输出中word作为键,文件名最为值            }        }    }

reduce类代码
reduce方法的主要作用是为每个单词创建单独的输出行,并列出包含该单词的文件名。一旦每个逐渐及其文件名被map方法列出,MR框架将会调用reducer,reduce方法需要执行的就是将文件ID合并到一起,并输出合并结果

public static class Reduce            extends Reducer<Text, Text, Text, Text> {//在定义reducer时需要指定输入和输出键值类型        private Text docIds = new Text();        public void reduce(Text key, Iterable<Text> values,Context context) throws IOException, InterruptedException {//每个独立的map输出键都要调用reduce方法,Iterable允许对同一个键对应的所有值进行迭代操作            HashSet<Text> uniqueDocIds = new HashSet<Text>();//将同一个键对应的文件名保存在一个Set中            for (Text docId : values) {//对同一个键对应的索引文件名进行迭代               uniqueDocIds.add(new Text(docId))//将文件名加入Set中,创建新Text对象的目的是MR在迭代values的时候,可以复用这个Text对象,即可以创建新的副本            }            docIds.set(new Text(StringUtils.join(uniqueDocIds,",")));            context.write(key, docIds);//reduce输出一个词,已经包含这个词的文件名列表,这个列表是以CSV格式进行分割的        }    }

驱动类代码

驱动类代码可以设置启动MR作业的所以需要的属性。需要在MR框架上定义map和reduce函数可以使用哪些类,并定义输出与输入结果的存储位置。
默认情况下,MR可以处理文本文件,如果需要处理复杂的文件结构,需要使用不同的数据存储技术,那么就需要定义MR框架如何读取这样的数据源。

  public static void main(String[] args) throws Exception {        Configuration conf = new Configuration();//为作业做的配置容器,map和reduce类可以从这里获取设置的任何值        String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();//输入是一个或多个文件,需要创建一个输入参数的子数组,包括数组的最后一个项目,即MR作业的输出目录        if (otherArgs.length < 2) {            System.err.println("Usage: wordcount <in> [<in>...] <out>");            System.exit(2);        }        Job job = Job.getInstance(conf, "word count");        job.setJarByClass(WordCount.class);//任务类的setJarByClass方法定义哪些Jar包需要通过Hadoop来同步复制到集群中,并在随后的任务中同时设置classpath引用传入的类,使得,MapReduce类可以被任务所调用        job.setMapperClass(Map.class);//为作业设置map类        job.setCombinerClass(Reduce.class);//为作业设置combine类        job.setReducerClass(Reduce.class);//为作业设置reduce类        job.setOutputKeyClass(Text.class);//如果map输出的键和值与输出类型不同时,需要告诉Hadoop它们输出的时候是什么类型        job.setOutputValueClass(Text.class);//设置map输出值的类        for (int i = 0; i < otherArgs.length - 1; ++i) {            FileInputFormat.addInputPath(job, new Path(otherArgs[i]));//设置作业HDFS的输入文件,Hadoop可设置多个输入文件,文件名使用逗号隔开        }        //另一种设置输入路径的方式        //FileInputFormat.setInputPath(job,StringUtils.join(otherArgs,","));        FileOutputFormat.setOutputPath(job,                new Path(otherArgs[otherArgs.length - 1]));//设置作业HDFS的输出目录               Path output = new Path(otherArgs[otherArgs.length - 1]) ;               output.getFileSystem(conf).delete(output.true);//如果在HDFS上存在这个目录,就把它删除,否则该目录的存在会导致作业失败        System.exit(job.waitForCompletion(true) ? 0 : 1);//通知集群运行这个作业,并阻塞直到作业完成    }
原创粉丝点击