1.解析Partition
Map的结果,会通过partition分发到Reducer上,Reducer做完Reduce操作后,通过OutputFormat,进行输出,下面我们就来分析参与这个过程的类。
Mapper的结果,可能送到Combiner做合并,Combiner在系统中并没有自己的基类,而是用Reducer作为Combiner的基类,他们对外的功能是一样的,只是使用的位置和使用时的上下文不太一样而已。Mapper最终处理的键值对<key,value>,是需要送到Reducer去合并的,合并的时候,有相同key的键/值对会送到同一个Reducer那。哪个key到哪个Reducer的分配过程,是由Partitioner规定的。它只有一个方法,
1. getPartition(Text key, Text value, int numPartitions)
输入是Map的结果对<key,value>和Reducer的数目,输出则是分配的Reducer(整数编号)。就是指定Mappr输出的键值对到哪一个reducer上去。系统缺省的Partitioner是HashPartitioner,它以key的Hash值对Reducer的数目取模,得到对应的Reducer。这样保证如果有相同的key值,肯定被分配到同一个reducre上。如果有N个reducer,编号就为0,1,2,3……(N-1)。
Reducer是所有用户定制Reducer类的基类,和Mapper类似,它也有setup,reduce,cleanup和run方法,其中setup和cleanup含义和Mapper相同,reduce是真正合并Mapper结果的地方,它的输入是key和这个key对应的所有value的一个迭代器,同时还包括Reducer的上下文。系统中定义了两个非常简单的Reducer,IntSumReducer和LongSumReducer,分别用于对整形/长整型的value求和。
Reduce的结果,通过Reducer.Context的方法collect输出到文件中,和输入类似,Hadoop引入了OutputFormat。OutputFormat依赖两个辅助接口:RecordWriter和OutputCommitter,来处理输出。RecordWriter提供了write方法,用于输出<key,value>和close方法,用于关闭对应的输出。OutputCommitter提供了一系列方法,用户通过实现这些方法,可以定制OutputFormat生存期某些阶段需要的特殊操作。我们在TaskInputOutputContext中讨论过这些方法(明显,TaskInputOutputContext是OutputFormat和Reducer间的桥梁)。OutputFormat和RecordWriter分别对应着InputFormat和RecordReader,系统提供了空输出NullOutputFormat(什么结果都不输出,NullOutputFormat.RecordWriter只是示例,系统中没有定义),LazyOutputFormat(没在类图中出现,不分析),FilterOutputFormat(不分析)和基于文件FileOutputFormat的SequenceFileOutputFormat和TextOutputFormat输出。
基于文件的输出FileOutputFormat利用了一些配置项配合工作,包括:
mapred.output.compress:是否压缩;
mapred.output.compression.codec:压缩方法;
mapred.output.dir:输出路径;
mapred.work.output.dir:输出工作路径。
FileOutputFormat还依赖于FileOutputCommitter,通过FileOutputCommitter提供一些和Job,Task相关的临时文件管理功能。如FileOutputCommitter的setupJob,会在输出路径下创建一个名为_temporary的临时目录,cleanupJob则会删除这个目录。
SequenceFileOutputFormat输出和TextOutputFormat输出分别对应输入的SequenceFileInputFormat和TextInputFormat。
1. package org.apache.hadoop.examples;
2.
3. import java.io.IOException;
4. import java.util.*;
5. import org.apache.hadoop.fs.Path;
6. import org.apache.hadoop.conf.*;
7. import org.apache.hadoop.io.*;
8. import org.apache.hadoop.mapred.*;
9. import org.apache.hadoop.util.*;
10.
11. /**
12. * 输入文本,以tab间隔
13. * kaka 1 28
14. * hua 0 26
15. * chao 1
16. * tao 1 22
17. * mao 0 29 22
18. * */
19.
20. //Partitioner函数的使用
21.
22. public class MyPartitioner {
23. // Map函数
24. public static class MyMap extends MapReduceBase implements
25. Mapper<LongWritable, Text, Text, Text> {
26. public void map(LongWritable key, Text value,
27. OutputCollector<Text, Text> output, Reporter reporter)
28. throws IOException {
29. String[] arr_value = value.toString().split("\t");
30. //测试输出
31. // for(int i=0;i<arr_value.length;i++)
32. // {
33. // System.out.print(arr_value[i]+"\t");
34. // }
35. // System.out.print(arr_value.length);
36. // System.out.println();
37. Text word1 = new Text();
38. Text word2 = new Text();
39. if (arr_value.length > 3) {
40. word1.set("long");
41. word2.set(value);
42. } else if (arr_value.length < 3) {
43. word1.set("short");
44. word2.set(value);
45. } else {
46. word1.set("right");
47. word2.set(value);
48. }
49. output.collect(word1, word2);
50. }
51. }
52.
53. public static class MyReduce extends MapReduceBase implements
54. Reducer<Text, Text, Text, Text> {
55. public void reduce(Text key, Iterator<Text> values,
56. OutputCollector<Text, Text> output, Reporter reporter)
57. throws IOException {
58. int sum = 0;
59. System.out.println(key);
60. while (values.hasNext()) {
61. output.collect(key, new Text(values.next().getBytes()));
62. }
63. }
64. }
65.
66. // 接口Partitioner继承JobConfigurable,所以这里有两个override方法
67. public static class MyPartitionerPar implements Partitioner<Text, Text> {
68. /**
69. * getPartition()方法的
70. * 输入参数:键/值对<key,value>与reducer数量numPartitions
71. * 输出参数:分配的Reducer编号,这里是result
72. * */
73. @Override
74. public int getPartition(Text key, Text value, int numPartitions) {
75. // TODO Auto-generated method stub
76. int result = 0;
77. System.out.println("numPartitions--" + numPartitions);
78. if (key.toString().equals("long")) {
79. result = 0 % numPartitions;
80. } else if (key.toString().equals("short")) {
81. result = 1 % numPartitions;
82. } else if (key.toString().equals("right")) {
83. result = 2 % numPartitions;
84. }
85. System.out.println("result--" + result);
86. return result;
87. }
88.
89. @Override
90. public void configure(JobConf arg0)
91. {
92. // TODO Auto-generated method stub
93. }
94. }
95.
96. //输入参数:/home/hadoop/input/PartitionerExample /home/hadoop/output/Partitioner
97. public static void main(String[] args) throws Exception {
98. JobConf conf = new JobConf(MyPartitioner.class);
99. conf.setJobName("MyPartitioner");
100.
101. //控制reducer数量,因为要分3个区,所以这里设定了3个reducer
102. conf.setNumReduceTasks(3);
103.
104. conf.setMapOutputKeyClass(Text.class);
105. conf.setMapOutputValueClass(Text.class);
106.
107. //设定分区类
108. conf.setPartitionerClass(MyPartitionerPar.class);
109.
110. conf.setOutputKeyClass(Text.class);
111. conf.setOutputValueClass(Text.class);
112.
113. //设定mapper和reducer类
114. conf.setMapperClass(MyMap.class);
115. conf.setReducerClass(MyReduce.class);
116.
117. conf.setInputFormat(TextInputFormat.class);
118. conf.setOutputFormat(TextOutputFormat.class);
119.
120. FileInputFormat.setInputPaths(conf, new Path(args[0]));
121. FileOutputFormat.setOutputPath(conf, new Path(args[1]));
122.
123. JobClient.runJob(conf);
124. }
125. }