1. wordcount程序
先以简单的wordcount为例。
Mapper:
- package cn.nuc.hadoop.mapreduce.wordcount;
-
- import java.io.IOException;
-
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
-
-
-
-
-
- public class WCMapper extends Mapper {
-
-
- @Override
- protected void map(LongWritable k1, Text v1,
- Mapper.Context context)
- throws IOException, InterruptedException {
-
- String line = v1.toString();
-
-
- String[] words = StringUtils.split(line, " ");
-
- for (String word : words) {
- context.write(new Text(word), new IntWritable(1));
- }
-
- }
-
- }
Reducer:
- package cn.nuc.hadoop.mapreduce.wordcount;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
-
-
-
- public class WCReducer extends Reducer {
-
-
-
- @Override
- protected void reduce(Text k2, Iterable v2s,
- Reducer.Context context)
- throws IOException, InterruptedException {
- int count = 0;
-
- for (IntWritable v2 : v2s) {
- count = v2.get();
- }
-
- context.write(k2, new IntWritable(count));
- }
-
- }
驱动类:
- package cn.nuc.hadoop.mapreduce.wordcount;
-
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
-
-
-
-
-
-
-
-
- public class WCRunner {
- public static void main(String[] args) throws ClassNotFoundException,
- InterruptedException, IOException {
-
- Configuration conf = new Configuration();
- Job wcJob = Job.getInstance(conf);
-
-
- wcJob.setJarByClass(WCRunner.class);
-
-
- wcJob.setMapperClass(WCMapper.class);
- wcJob.setReducerClass(WCReducer.class);
-
-
-
-
-
-
-
- wcJob.setOutputKeyClass(Text.class);
- wcJob.setOutputValueClass(IntWritable.class);
-
-
- FileInputFormat.setInputPaths(wcJob, new Path(
- "hdfs://master:9000/user/exe_mapreduce/wordcount/input"));
-
- FileOutputFormat.setOutputPath(wcJob, new Path(
- "hdfs://master:9000/user/exe_mapreduce/wordcount/output"));
-
-
- wcJob.waitForCompletion(true);
- }
- }
2. 统计手机流量信息
从下面的数据中的得到每个手机号的上行流量、下行流量、总流量。
源数据:
- 1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 200
- 1363157995052 13826544101 5C-0E-8B-C7-F1-E0:CMCC 120.197.40.4 4 0 264 0 200
- 1363157991076 13926435656 20-10-7A-28-CC-0A:CMCC 120.196.100.99 2 4 132 1512 200
- 1363154400022 13926251106 5C-0E-8B-8B-B1-50:CMCC 120.197.40.4 4 0 240 0 200
- 1363157993044 18211575961 94-71-AC-CD-E6-18:CMCC-EASY 120.196.100.99 iface.qiyi.com 视频网站 15 12 1527 2106 200
- 1363157995074 84138413 5C-0E-8B-8C-E8-20:7DaysInn 120.197.40.4 122.72.52.12 20 16 4116 1432 200
- 1363157993055 13560439658 C4-17-FE-BA-DE-D9:CMCC 120.196.100.99 18 15 1116 954 200
- 1363157995033 15920133257 5C-0E-8B-C7-BA-20:CMCC 120.197.40.4 sug.so.360.cn 信息安全 20 20 3156 2936 200
- 1363157983019 13719199419 68-A1-B7-03-07-B1:CMCC-EASY 120.196.100.82 4 0 240 0 200
- 1363157984041 13660577991 5C-0E-8B-92-5C-20:CMCC-EASY 120.197.40.4 s19.cnzz.com 站点统计 24 9 6960 690 200
- 1363157973098 15013685858 5C-0E-8B-C7-F7-90:CMCC 120.197.40.4 rank.ie.sogou.com 搜索引擎 28 27 3659 3538 200
- 1363157986029 15989002119 E8-99-C4-4E-93-E0:CMCC-EASY 120.196.100.99 www.umeng.com 站点统计 3 3 1938 180 200
- 1363157992093 13560439658 C4-17-FE-BA-DE-D9:CMCC 120.196.100.99 15 9 918 4938 200
- 1363157986041 13480253104 5C-0E-8B-C7-FC-80:CMCC-EASY 120.197.40.4 3 3 180 180 200
- 1363157984040 13602846565 5C-0E-8B-8B-B6-00:CMCC 120.197.40.4 2052.flash2-http.qq.com 综合门户 15 12 1938 2910 200
- 1363157995093 13922314466 00-FD-07-A2-EC-BA:CMCC 120.196.100.82 img.qfc.cn 12 12 3008 3720 200
- 1363157982040 13502468823 5C-0A-5B-6A-0B-D4:CMCC-EASY 120.196.100.99 y0.ifengimg.com 综合门户 57 102 7335 110349 200
- 1363157986072 18320173382 84-25-DB-4F-10-1A:CMCC-EASY 120.196.100.99 input.shouji.sogou.com 搜索引擎 21 18 9531 2412 200
- 1363157990043 13925057413 00-1F-64-E1-E6-9A:CMCC 120.196.100.55 t3.baidu.com 搜索引擎 69 63 11058 48243 200
- 1363157988072 13760778710 00-FD-07-A4-7B-08:CMCC 120.196.100.82 2 2 120 120 200
- 1363157985066 13726238888 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 200
- 1363157993055 13560436666 C4-17-FE-BA-DE-D9:CMCC 120.196.100.99 18 15 1116 954 200
数据格式为:
想要的到的数据格式为:
- 手机号 上行流量 下行流量 总流量
- 13726230503 2481 24681 27162
- 13826544101 264 0 264
- 13926435656 132 1512 1644
- ... ... ...
2.1 引入和Hadoop序列化机制相关的mapreduce
由于源数据中每一个手机号可能存在多条上网记录,最后要得到的输出格式是一个手机号的所有上行流量、下行流量和总流量。所以可以考虑利用MapReduce框架的特性,将每个手机号作为map的输出key,该手机号上网信息作为map的输出value,经过shuffle,则在reduce端接收到一个,其中,key手机号,value-list为该手机号所对应的一些上网信息的集合。这里有一个问题,由于map和reduce输入输出都为key-value键值对形式,所以必须将手机的上网信息(上行流量、下行流量)封装成一个Bean类,将这个类作为value。
由于数据需要在不同的节点间进行网络传输,所以Bean类必须实现序列化和反序列化,Hadoop提供了一套序列化机制(实现Writable接口)
FlowBean:
- package cn.nuc.hadoop.mapreduce.flowsum;
-
- import java.io.DataInput;
- import java.io.DataOutput;
- import java.io.IOException;
-
- import org.apache.hadoop.io.Writable;
-
- public class FlowBean implements Writable {
-
- private String phoneNB;
- private long up_flow;
- private long down_flow;
- private long sum_flow;
-
-
- public FlowBean() {
- }
-
-
- public FlowBean(String phoneNB, long up_flow, long down_flow) {
- this.phoneNB = phoneNB;
- this.up_flow = up_flow;
- this.down_flow = down_flow;
- this.sum_flow = up_flow + down_flow;
- }
-
-
- @Override
- public void write(DataOutput out) throws IOException {
- out.writeUTF(phoneNB);
- out.writeLong(up_flow);
- out.writeLong(down_flow);
- out.writeLong(sum_flow);
- }
-
-
-
- @Override
- public void readFields(DataInput in) throws IOException {
- this.phoneNB = in.readUTF();
- this.up_flow = in.readLong();
- this.down_flow = in.readLong();
- this.sum_flow = in.readLong();
- }
-
- public String getPhoneNB() {
- return phoneNB;
- }
-
- public void setPhoneNB(String phoneNB) {
- this.phoneNB = phoneNB;
- }
-
- public long getUp_flow() {
- return up_flow;
- }
-
- public void setUp_flow(long up_flow) {
- this.up_flow = up_flow;
- }
-
- public long getDown_flow() {
- return down_flow;
- }
-
- public void setDown_flow(long down_flow) {
- this.down_flow = down_flow;
- }
-
- public long getSum_flow() {
- return sum_flow;
- }
-
- public void setSum_flow(long sum_flow) {
- this.sum_flow = sum_flow;
- }
-
- @Override
- public String toString() {
- return "" + up_flow + "\t" + down_flow + "\t" + sum_flow;
- }
- }
FlowSumMapper:
- package cn.nuc.hadoop.mapreduce.flowsum;
-
- import java.io.IOException;
-
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
-
- public class FlowSumMapper extends Mapper {
-
- @Override
- protected void map(LongWritable k1, Text v1,
- Mapper.Context context)
- throws IOException, InterruptedException {
-
- String line = v1.toString();
-
- String[] fields = StringUtils.split(line, "\t");
-
- String phoneNB = fields[1];
- long up_flow = Long.parseLong(fields[7]);
- long down_flow = Long.parseLong(fields[8]);
-
- context.write(new Text(phoneNB), new FlowBean(phoneNB, up_flow,
- down_flow));
-
- }
- }
FlowSumReducer:
- package cn.nuc.hadoop.mapreduce.flowsum;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
-
- public class FlowSumReducer extends Reducer {
-
-
-
- @Override
- protected void reduce(Text k2, Iterable v2s,
- Reducer.Context context)
- throws IOException, InterruptedException {
- long up_flow = 0;
- long down_flow = 0;
-
- for (FlowBean v2 : v2s) {
- up_flow += v2.getUp_flow();
- down_flow += v2.getDown_flow();
- }
-
- context.write(k2, new FlowBean(k2.toString(), up_flow, down_flow));
- }
- }
FlowSumRunner:
Job描述和提交的规范写法如下:
- package cn.nuc.hadoop.mapreduce.flowsum;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.conf.Configured;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
- import org.apache.hadoop.util.Tool;
- import org.apache.hadoop.util.ToolRunner;
-
-
- public class FlowSumRunner extends Configured implements Tool {
-
- public static void main(String[] args) throws Exception {
- int res = ToolRunner
- .run(new Configuration(), new FlowSumRunner(), args);
- System.exit(res);
- }
-
- @Override
- public int run(String[] args) throws Exception {
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(FlowSumRunner.class);
-
- job.setMapperClass(FlowSumMapper.class);
- job.setReducerClass(FlowSumReducer.class);
-
-
-
-
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(FlowBean.class);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
-
-
- return job.waitForCompletion(true) ? 0 : 1;
- }
- }
打成jar包后运行:
- [hadoop@master ~]$ hadoop jar flowcount.jar cn.nuc.hadoop.mapreduce.flowsum.FlowSumRunner /user/exe_mapreduce/flowcount/input /user/exe_mapreduce/flowcount/output
查看结果:
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/output/part-r-00000
- 13480253104 180 200 380
- 13502468823 102 7335 7437
- 13560436666 954 200 1154
- 13560439658 5892 400 6292
- 13602846565 12 1938 1950
- 13660577991 9 6960 6969
- 13719199419 0 200 200
- 13726230503 2481 24681 27162
- 13726238888 2481 24681 27162
- 13760778710 120 200 320
- 13826544101 0 200 200
- 13922314466 3008 3720 6728
- 13925057413 63 11058 11121
- 13926251106 0 200 200
- 13926435656 1512 200 1712
- 15013685858 27 3659 3686
- 15920133257 20 3156 3176
- 15989002119 3 1938 1941
- 18211575961 12 1527 1539
- 18320173382 18 9531 9549
- 84138413 4116 1432 5548
2.2 引入hadoop自定义排序
从上面得到的结果可以看出来,hadoop默认将结果按照mapper的输出按照key来进行排序,如果我们想要自定义排序结果(比如按照总流量从高到低排序),该如何做呢?了解shuffle的都知道,shuffle过程中,会将map的输出结果按照key进行排序,所以只需要将FlowBean作为map输出的key值,前提是FlowBean实现了Comparable接口。在hadoop中既实现Writable接口,又实现Comparable接口,可以简写为实现了WritableComparable接口。
FlowBean:
- package cn.nuc.hadoop.mapreduce.flowsort;
-
- import java.io.DataInput;
- import java.io.DataOutput;
- import java.io.IOException;
-
- import org.apache.hadoop.io.WritableComparable;
-
- public class FlowBean implements WritableComparable {
-
- private String phoneNB;
- private long up_flow;
- private long down_flow;
- private long sum_flow;
-
-
- public FlowBean() {
- }
-
-
- public FlowBean(String phoneNB, long up_flow, long down_flow) {
- this.phoneNB = phoneNB;
- this.up_flow = up_flow;
- this.down_flow = down_flow;
- this.sum_flow = up_flow + down_flow;
- }
-
-
- @Override
- public void write(DataOutput out) throws IOException {
- out.writeUTF(phoneNB);
- out.writeLong(up_flow);
- out.writeLong(down_flow);
- out.writeLong(sum_flow);
- }
-
-
-
- @Override
- public void readFields(DataInput in) throws IOException {
- this.phoneNB = in.readUTF();
- this.up_flow = in.readLong();
- this.down_flow = in.readLong();
- this.sum_flow = in.readLong();
- }
-
- public String getPhoneNB() {
- return phoneNB;
- }
-
- public void setPhoneNB(String phoneNB) {
- this.phoneNB = phoneNB;
- }
-
- public long getUp_flow() {
- return up_flow;
- }
-
- public void setUp_flow(long up_flow) {
- this.up_flow = up_flow;
- }
-
- public long getDown_flow() {
- return down_flow;
- }
-
- public void setDown_flow(long down_flow) {
- this.down_flow = down_flow;
- }
-
- public long getSum_flow() {
- return sum_flow;
- }
-
- public void setSum_flow(long sum_flow) {
- this.sum_flow = sum_flow;
- }
-
- @Override
- public String toString() {
- return "" + up_flow + "\t" + down_flow + "\t" + sum_flow;
- }
-
-
- @Override
- public int compareTo(FlowBean o) {
- return this.sum_flow > o.sum_flow ? -1 : 1;
- }
- }
SortMapReduce:
- package cn.nuc.hadoop.mapreduce.flowsort;
-
- import java.io.IOException;
-
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.NullWritable;
- import org.apache.hadoop.io.Text;
- 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.output.FileOutputFormat;
-
- public class SortMapReduce {
-
- public static class SortMapper extends
- Mapper {
- @Override
- protected void map(
- LongWritable k1,
- Text v1,
- Mapper.Context context)
- throws IOException, InterruptedException {
-
- String line = v1.toString();
- String[] fields = StringUtils.split(line, "\t");
-
- String phoneNB = fields[0];
- long up_flow = Long.parseLong(fields[1]);
- long down_flow = Long.parseLong(fields[2]);
-
- context.write(new FlowBean(phoneNB, up_flow, down_flow),
- NullWritable.get());
- }
- }
-
- public static class SortReducer extends
- Reducer {
- @Override
- protected void reduce(FlowBean k2, Iterable v2s,
- Reducer.Context context)
- throws IOException, InterruptedException {
- String phoneNB = k2.getPhoneNB();
- context.write(new Text(phoneNB), k2);
- }
- }
-
- public static void main(String[] args) throws IOException,
- ClassNotFoundException, InterruptedException {
-
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(SortMapReduce.class);
-
- job.setMapperClass(SortMapper.class);
- job.setReducerClass(SortReducer.class);
-
- job.setMapOutputKeyClass(FlowBean.class);
- job.setMapOutputValueClass(NullWritable.class);
-
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(FlowBean.class);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
-
- System.exit(job.waitForCompletion(true) ? 0 : 1);
- }
- }
打成jar包,运行:
- [hadoop@master ~]$ hadoop jar flowcountsort.jar cn.nuc.hadoop.mapreduce.flowsort.SortMapReduce /user/exe_mapreduce/flowcount/output /user/exe_mapreduce/flowcount/sortout/
查看结果:
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/sortout/part-r-00000
- 13726238888 2481 24681 27162
- 13726230503 2481 24681 27162
- 13925057413 63 11058 11121
- 18320173382 18 9531 9549
- 13502468823 102 7335 7437
- 13660577991 9 6960 6969
- 13922314466 3008 3720 6728
- 13560439658 5892 400 6292
- 84138413 4116 1432 5548
- 15013685858 27 3659 3686
- 15920133257 20 3156 3176
- 13602846565 12 1938 1950
- 15989002119 3 1938 1941
- 13926435656 1512 200 1712
- 18211575961 12 1527 1539
- 13560436666 954 200 1154
- 13480253104 180 200 380
- 13760778710 120 200 320
- 13826544101 0 200 200
- 13926251106 0 200 200
- 13719199419 0 200 200
2.3 引入Hadoop分区功能
如果信息特别多,想要将最后的结果分别存放在不通过的文件中,该怎么办呢?可以使用Hadoop提供的Partitioner函数,hadoop默认使用HashPartitioner。可以查看下Hadoop源码:
- public class HashPartitioner extends Partitioner {
-
-
- public int getPartition(K key, V value,
- int numReduceTasks) {
- return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
- }
-
- }
HashPartitioner是处理Mapper任务输出的,getPartition()方法有三个形参,key、value分别指的是Mapper任务的输出,numReduceTasks指的是设置的Reducer任务数量,默认值是1。那么任何整数与1相除的余数肯定是0。也就是说getPartition(…)方法的返回值总是0。也就是Mapper任务的输出总是送给一个Reducer任务,最终只能输出到一个文件中。据此分析,如果想要最终输出到多个文件中,在Mapper任务中对数据应该划分到多个区中。
AreaPartitioner
- package cn.nuc.hadoop.mapreduce.areapartition;
-
- import java.util.HashMap;
-
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Partitioner;
-
- public class AreaPartitioner extends Partitioner {
-
- private static HashMap areaMap = new HashMap<>();
-
- static {
- areaMap.put("135", 0);
- areaMap.put("136", 1);
- areaMap.put("137", 2);
- areaMap.put("138", 3);
- areaMap.put("139", 4);
- }
-
- @Override
- public int getPartition(Text key, FlowBean value, int numPartitions) {
-
- Integer areCoder = areaMap.get(key.toString().substring(0, 3));
- if (areCoder == null) {
- areCoder = 5;
- }
- return areCoder;
- }
-
- }
FlowBean
- package cn.nuc.hadoop.mapreduce.areapartition;
-
- import java.io.DataInput;
- import java.io.DataOutput;
- import java.io.IOException;
-
- import org.apache.hadoop.io.WritableComparable;
-
- public class FlowBean implements WritableComparable {
-
- private String phoneNB;
- private long up_flow;
- private long down_flow;
- private long sum_flow;
-
-
- public FlowBean() {
- }
-
-
- public FlowBean(String phoneNB, long up_flow, long down_flow) {
- this.phoneNB = phoneNB;
- this.up_flow = up_flow;
- this.down_flow = down_flow;
- this.sum_flow = up_flow + down_flow;
- }
-
-
- @Override
- public void write(DataOutput out) throws IOException {
- out.writeUTF(phoneNB);
- out.writeLong(up_flow);
- out.writeLong(down_flow);
- out.writeLong(sum_flow);
- }
-
-
-
- @Override
- public void readFields(DataInput in) throws IOException {
- this.phoneNB = in.readUTF();
- this.up_flow = in.readLong();
- this.down_flow = in.readLong();
- this.sum_flow = in.readLong();
- }
-
- public String getPhoneNB() {
- return phoneNB;
- }
-
- public void setPhoneNB(String phoneNB) {
- this.phoneNB = phoneNB;
- }
-
- public long getUp_flow() {
- return up_flow;
- }
-
- public void setUp_flow(long up_flow) {
- this.up_flow = up_flow;
- }
-
- public long getDown_flow() {
- return down_flow;
- }
-
- public void setDown_flow(long down_flow) {
- this.down_flow = down_flow;
- }
-
- public long getSum_flow() {
- return sum_flow;
- }
-
- public void setSum_flow(long sum_flow) {
- this.sum_flow = sum_flow;
- }
-
- @Override
- public String toString() {
- return "" + up_flow + "\t" + down_flow + "\t" + sum_flow;
- }
-
-
- @Override
- public int compareTo(FlowBean o) {
- return this.sum_flow > o.sum_flow ? -1 : 1;
- }
- }
FlowSumArea
- package cn.nuc.hadoop.mapreduce.areapartition;
-
- import java.io.IOException;
-
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- 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.output.FileOutputFormat;
-
-
-
-
-
-
-
-
- public class FlowSumArea {
-
- public static class FlowSumAreaMapper extends
- Mapper {
-
- @Override
- protected void map(LongWritable k1, Text v1,
- Mapper.Context context)
- throws IOException, InterruptedException {
- String line = v1.toString();
- String[] fields = StringUtils.split(line, "\t");
-
- String phoneNB = fields[1];
- Long up_flow = Long.parseLong(fields[7]);
- Long down_flow = Long.parseLong(fields[8]);
-
- context.write(new Text(phoneNB), new FlowBean(phoneNB, up_flow,
- down_flow));
- }
- }
-
- public static class FlowSumAreaReducer extends
- Reducer {
-
- @Override
- protected void reduce(Text k2, Iterable v2s,
- Reducer.Context context)
- throws IOException, InterruptedException {
- long up_flow = 0;
- long down_flow = 0;
- for (FlowBean v2 : v2s) {
- up_flow += v2.getUp_flow();
- down_flow += v2.getDown_flow();
- }
- context.write(new Text(k2), new FlowBean(k2.toString(), up_flow,
- down_flow));
- }
- }
-
- public static void main(String[] args) throws IOException,
- ClassNotFoundException, InterruptedException {
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(FlowSumArea.class);
-
- job.setMapperClass(FlowSumAreaMapper.class);
- job.setReducerClass(FlowSumAreaReducer.class);
-
-
- job.setPartitionerClass(AreaPartitioner.class);
-
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(FlowBean.class);
-
-
- job.setNumReduceTasks(6);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
-
- System.exit(job.waitForCompletion(true) ? 0 : 1);
- }
- }
打包运行:
- [hadoop@master ~]$ hadoop jar area.jar cn.nuc.hadoop.mapreduce.areapartition.FlowSumArea /user/exe_mapreduce/flowcount/input /user/exe_mapreduce/flowcount/areaout
查看结果:
- [hadoop@master ~]$ hadoop fs -ls /user/exe_mapreduce/flowcount/areaout/
- Found 7 items
- -rw-r
- -rw-r
- -rw-r
- -rw-r
- -rw-r
- -rw-r
- -rw-r
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/areaout/part-r-00000
- 13502468823 102 7335 7437
- 13560436666 954 200 1154
- 13560439658 5892 400 6292
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/areaout/part-r-00001
- 13602846565 12 1938 1950
- 13660577991 9 6960 6969
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/areaout/part-r-00002
- 13719199419 0 200 200
- 13726230503 2481 24681 27162
- 13726238888 2481 24681 27162
- 13760778710 120 200 320
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/areaout/part-r-00003
- ^[[A13826544101 0 200 200
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/areaout/part-r-00004
- ^[[A13922314466 3008 3720 6728
- 13925057413 63 11058 11121
- 13926251106 0 200 200
- 13926435656 1512 200 1712
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/flowcount/areaout/part-r-00005
- 13480253104 180 200 380
- 15013685858 27 3659 3686
- 15920133257 20 3156 3176
- 15989002119 3 1938 1941
- 18211575961 12 1527 1539
- 18320173382 18 9531 9549
- 84138413 4116 1432 5548
3. 数据去重
“ 数据去重”主要是为了掌握和利用并行化思想来对数据进行有意义的筛选。 统计大 数据集上的数据种类个数、 从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据 去重。下面就进入这个实例的 MapReduce 程序设计。
3.1 实例描述
对数据文件中的数据进行去重。数据文件中的每行都是一个数据。
样例输入如下所示:
file1:
- 2012-3-1 a
- 2012-3-2 b
- 2012-3-3 c
- 2012-3-4 d
- 2012-3-5 a
- 2012-3-6 b
- 2012-3-7 c
- 2012-3-3 c
file2:
- 2012-3-1 b
- 2012-3-2 a
- 2012-3-3 b
- 2012-3-4 d
- 2012-3-5 a
- 2012-3-6 c
- 2012-3-7 d
- 2012-3-3 c
样例输出如下:
- 2012-3-1 a
- 2012-3-1 b
- 2012-3-2 a
- 2012-3-2 b
- 2012-3-3 b
- 2012-3-3 c
- 2012-3-4 d
- 2012-3-5 a
- 2012-3-6 b
- 2012-3-6 c
- 2012-3-7 c
- 2012-3-7 d
3.2 设计思路
数据去重的最终目标是让原始数据中出现次数超过一次的数据在输出文件中只出现一次。我们自然而然会想到将同一个数据的所有记录都交给一台 reduce 机器,无论这个数据出现多少次,只要在最终结果中输出一次就可以了。具体就是 reduce 的输入应该以数据作为 key,而对 value-list 则没有要求。当 reduce 接收到一个时就直接将 key复制到输出的 key 中,并将 value 设置成空值。
在 MapReduce 流程中, map 的输出经过 shuffle 过程聚集成后会交给 reduce。所以从设计好的 reduce 输入可以反推出 map 的输出 key 应为数据, value任意。继续反推, map 输出数据的 key 为数据,而在这个实例中每个数据代表输入文件中的一行内容,所以 map 阶段要完成的任务就是在采用 Hadoop 默认的作业输入方式之后,将value 设置为 key,并直接输出(输出中的 value 任意)。 map 中的结果经过 shuffle 过程之后交给 reduce。 reduce 阶段不会管每个 key 有多少个 value,它直接将输入的 key 复制为输出的 key,并输出就可以了(输出中的 value 被设置成空了)。
3.3 程序代码
DedupMapper:
- package cn.nuc.hadoop.mapreduce.dedup;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.NullWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
-
- public class DedupMapper extends Mapper {
-
- private static Text field = new Text();
-
- @Override
- protected void map(LongWritable key, Text value, Context context)
- throws IOException, InterruptedException {
-
- field = value;
- context.write(field, NullWritable.get());
-
- }
- }
DedupReducer:
- package cn.nuc.hadoop.mapreduce.dedup;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.NullWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
-
- public class DedupReducer extends
- Reducer {
- @Override
- protected void reduce(Text key, Iterable values,
- Context context) throws IOException, InterruptedException {
-
- context.write(key, NullWritable.get());
-
- }
- }
DedupRunner:
- package cn.nuc.hadoop.mapreduce.dedup;
-
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.NullWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
-
- public class DedupRunner {
- public static void main(String[] args) throws IOException,
- ClassNotFoundException, InterruptedException {
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(DedupRunner.class);
-
- job.setMapperClass(DedupMapper.class);
- job.setReducerClass(DedupReducer.class);
-
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(NullWritable.class);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
-
- job.waitForCompletion(true);
- }
- }
打成jar包后运行:
- [hadoop@master ~]$ hadoop jar dedup.jar cn.nuc.hadoop.mapreduce.dedup.DedupRunner /user/exe_mapreduce/dedup/input /user/exe_mapreduce/dedup/out
查看结果:
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/dedup/output/part-r-00000
- 2012-3-1 a
- 2012-3-1 b
- 2012-3-2 a
- 2012-3-2 b
- 2012-3-3 b
- 2012-3-3 c
- 2012-3-4 d
- 2012-3-5 a
- 2012-3-6 b
- 2012-3-6 c
- 2012-3-7 c
- 2012-3-7 d
4. 数据排序
“ 数据排序”是许多实际任务执行时要完成的第一项工作,比如学生成绩评比、数据建立索引等。这个实例和数据去重类似,都是先对原始数据进行初步处理,为进一步的数据 操作打好基础。下面进入这个示例。
4.1 实例描述
对输入文件中数据进行排序。 输入文件中的每行内容均为一个数字, 即一个数据。要求在输出中每行有两个间隔的数字,其中, 第一个代表原始数据在原始数据集中的位次, 第 二个代表原始数据。
样例输入:
file1:
file2:
file3:
样例输出:
- 1 2
- 2 6
- 3 15
- 4 22
- 5 26
- 6 32
- 7 32
- 8 54
- 9 92
- 10 650
- 11 654
- 12 756
- 13 5956
- 14 65223
4.2 设计思路
这个实例仅仅要求对输入数据进行排序,熟悉 MapReduce 过程的读者会很快想到在 MapReduce 过程中就有排序,是否可以利用这个默认的排序,而不需要自己再实现具体的 排序呢?答案是肯定的。
但是在使用之前首先需要了解它的默认排序规则。它是按照 key 值进行排序的,如果 key 为封装 int 的 IntWritable 类型,那么 MapReduce 按照数字大小对 key 排序,如果 key 为封装为 String 的 Text 类型,那么 MapReduce 按照字典顺序对字符串排序。
了解了这个细节,我们就知道应该使用封装 int 的 IntWritable 型数据结构了。也就是在 map 中将读入的数据转化成 IntWritable 型,然后作为 key 值输出( value 任意)。 reduce 拿到 之后,将输入的 key 作为 value 输出,并根据 value-list 中元素的个数决定 输出的次数。输出的 key(即代码中的 linenum)是一个全局变量,它统计当前 key 的位次。 需要注意的是这个程序中没有配置 Combiner,也就是在 MapReduce 过程中不使用 Combiner。 这主要是因为使用 map 和 reduce 就已经能够完成任务了。
4.3 程序代码
SortMapper:
- package cn.nuc.hadoop.mapreduce.sort;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
-
-
- public class SortMapper extends
- Mapper {
-
- private static IntWritable data = new IntWritable();
- private static final IntWritable one = new IntWritable(1);
-
- @Override
- protected void map(LongWritable key, Text value, Context context)
- throws IOException, InterruptedException {
- String line = value.toString();
- data.set(Integer.parseInt(line));
- context.write(data, one);
- }
- }
SortReducer:
- package cn.nuc.hadoop.mapreduce.sort;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.mapreduce.Reducer;
-
-
-
-
- public class SortReducer extends
- Reducer {
-
- private static IntWritable linenumber = new IntWritable(1);
-
- @Override
- protected void reduce(IntWritable key, Iterable values,
- Context context) throws IOException, InterruptedException {
- for (IntWritable value : values) {
- context.write(linenumber, key);
- linenumber.set(linenumber.get() + 1);
-
- }
-
- }
- }
SotrRunner:
- package cn.nuc.hadoop.mapreduce.sort;
-
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
-
- public class SortRunner {
- public static void main(String[] args) throws IllegalArgumentException,
- IOException, ClassNotFoundException, InterruptedException {
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(SortRunner.class);
-
- job.setMapperClass(SortMapper.class);
- job.setReducerClass(SortReducer.class);
-
- job.setOutputKeyClass(IntWritable.class);
- job.setOutputValueClass(IntWritable.class);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
-
- job.waitForCompletion(true);
- }
- }
打成jar包运行:
- [hadoop@master ~]$ hadoop jar sort.jar cn.nuc.hadoop.mapreduce.sort.SortRunner /user/exe_mapreduce/sort/input /user/exe_mapreduce/sort/output
查看结果:
- [hadoop@master input]$ hadoop fs -cat /user/exe_mapreduce/sort/output/part-r-00000
- 1 2
- 2 6
- 3 15
- 4 22
- 5 26
- 6 32
- 7 32
- 8 54
- 9 92
- 10 650
- 11 654
- 12 756
- 13 5956
- 14 65223
5 平均成绩
“平均成绩”主要目的还是在重温经典“ WordCount”例子,可以说是在基础上的微变 化版,该实例主要就是实现一个计算学生平均成绩的例子。
5.1 实例描述
对输入文件中数据进行就算学生平均成绩。输入文件中的每行内容均为一个学生的姓名 和他相应的成绩,如果有多门学科,则每门学科为一个文件。要求在输出中每行有两个间隔 的数据,其中, 第一个代表学生的姓名, 第二个代表其平均成绩。
样本输入:
math:
china:
english:
样本输出:
5.2 设计思路
计算学生平均成绩是一个仿“ WordCount”例子,用来重温一下开发 MapReduce 程序的 流程。程序包括两部分的内容: Map 部分和 Reduce 部分,分别实现了 map 和 reduce 的功能。
Map 处理的是一个纯文本文件,文件中存放的数据时每一行表示一个学生的姓名和他 相应一科成绩。 Mapper 处理的数据是由 InputFormat 分解过的数据集,其中 InputFormat 的 作用是将数据集切割成小数据集 InputSplit,每一个 InputSlit 将由一个 Mapper 负责处理。此 外,InputFormat 中还提供了一个 RecordReader 的实现,并将一个 InputSplit 解析成对提供给了 map 函数。 InputFormat 的默认值是 TextInputFormat,它针对文本文件,按行将 文本切割成 InputSlit,并用 LineRecordReader 将 InputSplit 解析成对, key 是行在 文本中的位置, value 是文件中的一行。
Map 的结果会通过 partion 分发到 Reducer, Reducer 做完 Reduce 操作后,将通过以格 式 OutputFormat 输出。
Mapper 最终处理的结果对,会送到 Reducer 中进行合并,合并的时候,有 相同 key 的键/值对则送到同一个 Reducer 上。 Reducer 是所有用户定制 Reducer 类地基础, 它的输入是 key 和这个 key 对应的所有 value 的一个迭代器,同时还有 Reducer 的上下文。 Reduce 的结果由 Reducer.Context 的 write 方法输出到文件中。
5.3 程序代码
ScoreMapper:
- package cn.nuc.hadoop.mapreduce.score;
-
- import java.io.IOException;
-
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
-
- public class ScoreMapper extends Mapper {
-
- private static Text name = new Text();
- private static IntWritable score = new IntWritable();
-
- @Override
- protected void map(LongWritable key, Text value, Context context)
- throws IOException, InterruptedException {
- String line = value.toString();
- String[] fields = StringUtils.split(line, " ");
- String strName = fields[0];
- int strScore = Integer.parseInt(fields[1]);
-
- name.set(strName);
- score.set(strScore);
- context.write(name, score);
- }
- }
ScoreReducer:
- package cn.nuc.hadoop.mapreduce.score;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
-
- public class ScoreReducer extends Reducer {
-
- private static IntWritable avg_score = new IntWritable();
-
- @Override
- protected void reduce(Text key, Iterable values,
- Context context) throws IOException, InterruptedException {
- int sum_score = 0;
- int count=0;
- for (IntWritable score : values) {
- count++;
- sum_score += score.get();
- }
-
- avg_score.set(sum_score / count);
- context.write(key, avg_score);
- }
- }
ScoreRunner:
- package cn.nuc.hadoop.mapreduce.score;
-
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
-
- public class ScoreRunner {
- public static void main(String[] args) throws IOException,
- ClassNotFoundException, InterruptedException {
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(ScoreRunner.class);
-
- job.setMapperClass(ScoreMapper.class);
- job.setReducerClass(ScoreReducer.class);
-
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(IntWritable.class);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
-
- job.waitForCompletion(true);
- }
- }
打成jar包执行:
- [hadoop@master ~]$ hadoop jar score.jar cn.nuc.hadoop.mapreduce.score.ScoreRunner /user/exe_mapreduce/score/input /user/exe_mapreduce/score/output
查看结果:
- [hadoop@master ~]$ hadoop fs -cat /user/exe_mapreduce/score/output/part-r-00000
- 张三 82
- 李四 90
- 王五 82
- 赵六 76
6 倒排索引
“ 倒排索引”是文档检索系统中最常用的数据结构,被广泛地应用于全文搜索引擎。 它主要是用来存储某个单词(或词组) 在一个文档或一组文档中的存储位置的映射,即提 供了一种根据内容来查找文档的方式。由于不是根据文档来确定文档所包含的内容,而是进 行相反的操作,因而称为倒排索引( Inverted Index)。
6.1 实例描述
通常情况下,倒排索引由一个单词(或词组)以及相关的文档列表组成,文档列表中的 文档或者是标识文档的 ID 号,或者是指文档所在位置的 URL,如图 6.1-1 所示。
从图 6.1-1 可以看出,单词 1 出现在{文档 1,文档 4,文档 13, ……}中,单词 2 出现 在{文档 3,文档 5,文档 15, ……}中,而单词 3 出现在{文档 1,文档 8,文档 20, ……} 中。在实际应用中, 还需要给每个文档添加一个权值,用来指出每个文档与搜索内容的相 关度,如图 6.1-2 所示。
最常用的是使用词频作为权重,即记录单词在文档中出现的次数。以英文为例,如图 6.1-3 所示,索引文件中的“ MapReduce”一行表示:“ MapReduce”这个单词在文本 T0 中 出现过 1 次,T1 中出现过 1 次,T2 中出现过 2 次。当搜索条件为“ MapReduce”、“ is”、“ Simple” 时,对应的集合为: {T0, T1, T2}∩{T0, T1}∩{T0, T1}={T0, T1},即文档 T0 和 T1 包 含了所要索引的单词,而且只有 T0 是连续的。
更复杂的权重还可能要记录单词在多少个文档中出现过,以实现 TF-IDF( Term Frequency-Inverse Document Frequency)算法,或者考虑单词在文档中的位置信息(单词是 否出现在标题中,反映了单词在文档中的重要性)等。
样例输入如下所示。
file1:
file2:
- MapReduce is powerful is simple
file3:
- Hello MapReduce bye MapReduce
样例输出如下所示:
- MapReduce file1.txt:1;file2.txt:1;file3.txt:2;
- is file1.txt:1;file2.txt:2;
- simple file1.txt:1;file2.txt:1;
- powerful file2.txt:1;
- Hello file3.txt:1;
- bye file3.txt:1;
6.2 设计思路
实现“ 倒排索引”只要关注的信息为: 单词、 文档 URL 及词频,如图 3-11 所示。但是 在实现过程中,索引文件的格式与图 6.1-3 会略有所不同,以避免重写 OutPutFormat 类。下 面根据 MapReduce 的处理过程给出倒排索引的设计思路。
1)Map过程
首先使用默认的 TextInputFormat 类对输入文件进行处理,得到文本中每行的偏移量及 其内容。显然, Map 过程首先必须分析输入的对,得到倒排索引中需要的三个信 息:单词、文档 URL 和词频,如图 6.2-1 所示。
这里存在两个问题: 第一, 对只能有两个值,在不使用 Hadoop 自定义数据 类型的情况下,需要根据情况将其中两个值合并成一个值,作为 key 或 value 值; 第二,通 过一个 Reduce 过程无法同时完成词频统计和生成文档列表,所以必须增加一个 Combine 过程完成词频统计。
这里讲单词和 URL 组成 key 值(如“ MapReduce: file1.txt”),将词频作为 value,这样 做的好处是可以利用 MapReduce 框架自带的 Map 端排序,将同一文档的相同单词的词频组 成列表,传递给 Combine 过程,实现类似于 WordCount 的功能。
2)Combine过程
经过 map 方法处理后, Combine 过程将 key 值相同的 value 值累加,得到一个单词在文 档在文档中的词频,如图 6.2-2 所示。 如果直接将图 6.2-2 所示的输出作为 Reduce 过程的输 入,在 Shuffle 过程时将面临一个问题:所有具有相同单词的记录(由单词、 URL 和词频组 成) 应该交由同一个 Reducer 处理,但当前的 key 值无法保证这一点,所以必须修改 key 值 和 value 值。这次将单词作为 key 值, URL 和词频组成 value 值(如“ file1.txt: 1”)。这样 做的好处是可以利用 MapReduce 框架默认的 HashPartitioner 类完成 Shuffle 过程,将相同单 词的所有记录发送给同一个 Reducer 进行处理。
3)Reduce过程
经过上述两个过程后, Reduce 过程只需将相同 key 值的 value 值组合成倒排索引文件所 需的格式即可,剩下的事情就可以直接交给 MapReduce 框架进行处理了。如图 6.2-3 所示。 索引文件的内容除分隔符外与图 6.1-3 解释相同。
4)需要解决的问题
本实例设计的倒排索引在文件数目上没有限制,但是单词文件不宜过大(具体值与默 认 HDFS 块大小及相关配置有关),要保证每个文件对应一个 split。否则,由于 Reduce 过 程没有进一步统计词频,最终结果可能会出现词频未统计完全的单词。可以通过重写 InputFormat 类将每个文件为一个 split,避免上述情况。或者执行两次 MapReduce, 第一次 MapReduce 用于统计词频, 第二次 MapReduce 用于生成倒排索引。除此之外,还可以利用 复合键值对等实现包含更多信息的倒排索引。
6.3 程序代码
InvertedIndexMapper:
- package cn.nuc.hadoop.mapreduce.invertedindex;
-
- import java.io.IOException;
-
- import org.apache.commons.lang.StringUtils;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
- import org.apache.hadoop.mapreduce.lib.input.FileSplit;
-
- public class InvertedIndexMapper extends Mapper {
-
- private static Text keyInfo = new Text();
- private static final Text valueInfo = new Text("1");
-
- @Override
- protected void map(LongWritable key, Text value, Context context)
- throws IOException, InterruptedException {
-
- String line = value.toString();
- String[] fields = StringUtils.split(line, " ");
-
- FileSplit fileSplit = (FileSplit) context.getInputSplit();
- String fileName = fileSplit.getPath().getName();
-
- for (String field : fields) {
-
- keyInfo.set(field + ":" + fileName);
- context.write(keyInfo, valueInfo);
- }
- }
- }
InvertedIndexCombiner:
- package cn.nuc.hadoop.mapreduce.invertedindex;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
-
- public class InvertedIndexCombiner extends Reducer {
-
- private static Text info = new Text();
-
-
-
- @Override
- protected void reduce(Text key, Iterable values, Context context)
- throws IOException, InterruptedException {
- int sum = 0;
- for (Text value : values) {
- sum += Integer.parseInt(value.toString());
- }
-
- int splitIndex = key.toString().indexOf(":");
-
- info.set(key.toString().substring(splitIndex + 1) + ":" + sum);
-
- key.set(key.toString().substring(0, splitIndex));
-
- context.write(key, info);
- }
- }
InvertedIndexReducer:
- package cn.nuc.hadoop.mapreduce.invertedindex;
-
- import java.io.IOException;
-
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
-
- public class InvertedIndexReducer extends Reducer {
-
- private static Text result = new Text();
-
-
-
- @Override
- protected void reduce(Text key, Iterable values, Context context)
- throws IOException, InterruptedException {
-
- String fileList = new String();
- for (Text value : values) {
- fileList += value.toString() + ";";
- }
-
- result.set(fileList);
- context.write(key, result);
- }
- }
InvertedIndexRunner:
- package cn.nuc.hadoop.mapreduce.invertedindex;
-
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
-
- public class InvertedIndexRunner {
- public static void main(String[] args) throws IOException,
- ClassNotFoundException, InterruptedException {
- Configuration conf = new Configuration();
- Job job = Job.getInstance(conf);
-
- job.setJarByClass(InvertedIndexRunner.class);
-
- job.setMapperClass(InvertedIndexMapper.class);
- job.setCombinerClass(InvertedIndexCombiner.class);
- job.setReducerClass(InvertedIndexReducer.class);
-
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(Text.class);
-
- FileInputFormat.setInputPaths(job, new Path(args[0]));
-
- Path output = new Path(args[1]);