Hadoop-Mapreduce-英文单词计数(Brief版本-超详细解读)

  1 package mapred;
  2 
  3 import java.io.IOException;
  4 import java.util.StringTokenizer;
  5 
  6 import org.apache.hadoop.conf.Configuration;
  7 import org.apache.hadoop.fs.FileSystem;
  8 import org.apache.hadoop.fs.Path;
  9 import org.apache.hadoop.io.IntWritable;
 10 import org.apache.hadoop.io.Text;
 11 import org.apache.hadoop.mapreduce.Job;
 12 import org.apache.hadoop.mapreduce.Mapper;
 13 import org.apache.hadoop.mapreduce.Reducer;
 14 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 15 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 16 import org.apache.hadoop.util.GenericOptionsParser;
 17 
 18 // 统计文本中单词个数:客户端、作业驱动类
 19 public class WordCountBrief {
 20 
 21     // 自定义Mapper静态内部Class 继承父类 Mapper类,进一步实现map过程
 22     // 这个Mapper静态内部类是一个泛型类型,它有4个形参类型(Object, Text, Text, IntWritable),
 23     // 分别指定map函数的输入键、输入值、输出键、输出值的类型。
 24     // Hadoop 没有直接使用Java内嵌的类型,而是自己开发了一套可以优化网络序列化传输的基本类型。
 25     // 这些类型都在org.apache.hadoop.io包中,比如,此处的Object类型,适用于字段需要使用多种类型的情况,
 26     // Text类型相当于Java中的String类型,IntWritable类型相当于Java中的Integer类型
 27     public static class TokenizerMapper extends
 28             Mapper<Object, Text, Text, IntWritable> {
 29 
 30         // 定义2个变量
 31         // 定义 value变量,并赋值1, 每个单词计数1次,map的输出value即为1
 32         private final static IntWritable one = new IntWritable(1);
 33         // 定义 key变量
 34         private Text word = new Text();
 35 
 36         // 实现 map 函数逻辑
 37         // Context是Mapper的一个内部类,用于在Map或Reduce任务中跟踪Task的状态。
 38         // MapContext记录了Map执行的上下文,在Mapper类中,Context可以存储一些Job conf的信息,
 39         // 比如Job运行时的参数等,可以在map函数中处理这些信息。同时,Context也充当了Map和Reduce任务
 40         // 执行过程中各个函数的桥梁,这与Java Web中的session对象、application对象很相似。简单说,
 41         // Context对象保存了Job运行时的上下文信息,比如作业的配置信息、InputSplit信息、任务ID等,此处用到了Context的write方法
 42         // 此处map函数,有3个形参类型(Object key, Text value, Context context),
 43         // 分别指定map函数的输入键、输入值、输出。
 44         public void map(Object key, Text value, Context context)
 45                 throws IOException, InterruptedException {
 46             // 构造一个用来解析输入value值的StringTokenizer对象
 47             StringTokenizer token = new StringTokenizer(value.toString());// Text类的value转化成字符串类型
 48             while (token.hasMoreTokens()) {// 循环取出token,并执行相关操作
 49                 // 返回从当前位置到下一个分割符的字符串,给结果变量赋值
 50                 word.set(token.nextToken());
 51                 // map过程输出键值对:输出每个被拆分出来的单词,以及计数1; 进行输出
 52                 context.write(word, one);
 53             }
 54         }
 55     }
 56 
 57     // 自定义Reducer Class 继承父类 Reducer类,进一步实现reduce过程
 58     // 这个Reducer静态内部类是一个泛型类型,它有4个形参类型(Text, IntWritable, Text, IntWritable),
 59     // 分别指定reduce函数的输入键、输入值、输出键、输出值的类型。
 60     public static class IntSumReducer extends
 61             Reducer<Text, IntWritable, Text, IntWritable> {
 62 
 63         // 定义结果变量
 64         private IntWritable result = new IntWritable();
 65 
 66         // 实现 reduce 函数逻辑
 67         // 此处reduce函数,有3个形参类型(Text key, Iterable<IntWritable> values, Context context),
 68         // 分别指定reduce函数的输入键、输入值列表或迭代器、输出。
 69         public void reduce(Text key, Iterable<IntWritable> values, Context context)
 70                 throws IOException, InterruptedException {
 71             // 做统计
 72             int sum = 0;
 73             for (IntWritable val : values) {
 74                 sum += val.get();
 75             }
 76             // 给结果变量赋值
 77             result.set(sum);
 78 
 79             // 进行输出
 80             context.write(key, result);
 81         }
 82     }
 83 
 84     // 输入args为: hdfs://localhost:9000/words hdfs://localhost:9000/wordcountout{参数1为要统计的文件,参数2为指定的输出位置(必须是不存在的)}
 85     public static void main(String[] args) throws Exception {
 86 
 87         // Configuration类代表作业的配置。该类会加载core-site.xml、hdfs-site.xml、mapred-site.xml、hdfs-site.xml等配置文件
 88         // 任何作用的配置信息必须通过Configuration传递,通过Configuration可以实现在多个mapper和多个reducer任务之间共享信息
 89         Configuration conf = new Configuration();
 90 
 91         // 解析参数
 92         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
 93         if (otherArgs.length < 2) {
 94             System.err.println("Usage: wordcountbrief <in> [<in>...] <out>");
 95             System.exit(2);
 96         }
 97 
 98         // 删除已经存在的输出目录
 99         Path mypath = new Path(otherArgs[otherArgs.length - 1]);// 输出路径
100         FileSystem hdfs = mypath.getFileSystem(conf);//获取hdfs文件系统
101         // 如果文件系统中存在这个输出路径,则删除掉,保证输出目录不能提前存在
102         if(hdfs.exists(mypath)){
103             hdfs.delete(mypath, true);// true 代表递归删除
104         }
105 
106         // 创建作业 job
107         // Job对象指定了作业执行规范,可以用来控制整个作业的运行
108         Job job = Job.getInstance(conf, "word count");
109 
110         // 通过传入的WordCountBrief类Class 设置job的jar包
111         // 在Hadoop集群上运行的作业时,要把代码打包成一个jar文件,然后把此文件传到集群上,
112         // 并通过命令来执行这个作业,但是命令中不必指定jar文件的名称。
113         // 在这条命令中,通过Job对象的setJarByClass方法传递一个主类即可,
114         // Hadoop会通过这个主类来查找包含它的jar包
115         job.setJarByClass(WordCountBrief.class);
116 
117         //  设置Mapper类Class
118         job.setMapperClass(TokenizerMapper.class);
119         //  设置Combine类Class
120         job.setCombinerClass(IntSumReducer.class);
121         //  设置Reducer类Class
122         job.setReducerClass(IntSumReducer.class);
123 
124         //  设置输出Key类Class
125         job.setOutputKeyClass(Text.class);
126         //  设置输出Value类Class
127         job.setOutputValueClass(IntWritable.class);
128         // 一般情况下,mapper和reducer输出的数据类型是一样的,所以可以用上面两条命令;
129         // 如果不一样,可以用下面两条命令单独指定mapper输出的key、value数据类型
130         // job.setMapOutputKeyClass(Text.class);
131         // job.setMapOutputValueClass(IntWritable.class);
132 
133         // Hadoop 默认的是TextInputFormat和TextOutputFormat,此处可以显示地配置
134         // job.setInputFormatClass(TextInputFormat.class);
135         // job.setOutputFormatClass(TextOutputFormat.class);
136 
137         // 指定输入文件
138         for (int i = 0; i < otherArgs.length - 1; ++i) {
139             // FileInputFormat指定的路径可以是单个文件、一个目录或符合特定文件模式的一系列文件
140             FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
141         }
142         // 指定输出位置
143         // 只能有一个输出路径,该路径指定的是reduce函数输出文件的写入目录。
144         // 特别注意:输出目录不能提前存在,否则Hadoop会报错并拒绝执行作业,这样做的目的是防止数据丢失
145         FileOutputFormat.setOutputPath(job, new Path(otherArgs[otherArgs.length - 1]));
146 
147         // 提交job作业
148         // waitForCompletion提交作业后,每秒会轮询作业进度,如果发现和上次报告后有改变,
149         // 就把进度报告到控制台,作业完成后,如果成功就显示作业计数,如果失败则把导致作业失败的错误输出到控制台
150         boolean result = job.waitForCompletion(true);
151 
152         System.exit(result ? 0 : 1);
153     }
154 }
155 // 查看指定路径文件
156 // hadoop fs -ls hdfs://localhost:9000/
157 // 返回结果,如下:
158 // Found 4 items
159 //-rw-r--r--   3 jiangshan supergroup  573545760 2021-09-08 15:48 hdfs://localhost:9000/SBSNTEST111.txt
160 //-rw-r--r--   3 jiangshan supergroup         28 2021-09-08 16:01 hdfs://localhost:9000/testcreate
161 //drwxr-xr-x   - jiangshan supergroup          0 2021-09-08 20:22 hdfs://localhost:9000/wordcountout
162 //-rw-r--r--   3 jiangshan supergroup  163254798 2021-09-08 16:56 hdfs://localhost:9000/words
163 
164 // 查看指定路径文件
165 //hadoop fs -ls hdfs://localhost:9000/wordcountout
166 //Found 2 items
167 //-rw-r--r--   3 jiangshan supergroup          0 2021-09-08 20:22 hdfs://localhost:9000/wordcountout/_SUCCESS
168 //-rw-r--r--   3 jiangshan supergroup      36850 2021-09-08 20:22 hdfs://localhost:9000/wordcountout/part-r-00000
169 
170 // 将路径指定文件的内容输出到stdout
171 // hadoop fs -cat hdfs://localhost:9000/wordcountout/part-r-00000
172 // 返回结果,文本文件内容
173 // Case    2400
174 // China   4800
175 // Clip    2400
176 // Colony  2400
177 // Comparison      4800
178 // ComponentValue  2400
179 // ....................

 

posted @ 2021-09-08 21:34  土博姜山山  阅读(273)  评论(0编辑  收藏  举报