mapper-reducer word count 实例

统计一个文件里各单词的个数,假设这个文件很大。

原理如下图:

mapper-reducer word count 实例_第1张图片

编写代码:

WCMapper.java

package zengmg.hadoop.mr.wordcount;

import java.io.IOException;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

/*
 * public class WCMapper extends Mapper{
 * 参数是4个泛型
 * KEYIN, VALUEIN,是mapper输入数据的类型
 * KEYOUT, VALUEOUT,是mapper输出数据的类型
 * map和reduce的数据输入输出都是以 key-value对的形式封装的
 * 默认情况下,框架传递给mapper的输入数据中,key是要处理的文本中一行的起始偏移量,这一行的内容作为value
 * mapper的输出类型要结合业务,这里输出 key是文本(String),value是个数(Long)
 * ps:
 * 输入和输出数据都要在网络上传递,这些数据都要序列化,java自带的序列化有太多的冗余内容,
 * 于是hadoop自定义了一套序列化。
 * 于是次数的java-Lang类型要用hadoop的LongWritable
 * java-String改为hadoop的Text
 */
public class WCMapper extends Mapper{
	
	/*
	 * mapper拿到一行数据,就调一次这个方法
	 */
	@Override
	protected void map(LongWritable key, Text value, Mapper.Context context)
			throws IOException, InterruptedException {
		//具体业务逻辑就写在这个方法里
		//需要处理的业务逻辑数据,框架已经传递进来了,就是方法中的key和value
		//key是要处理的文本中一行的起始偏移量,这一行的内容作为value
		//业务逻辑:
		//1、按空格分隔一行文本里的单词
		//2、输出 k 单词,v 1
		String line=value.toString();
		String[] words=line.split(" ");
		for(String word:words){
			context.write(new Text(word), new LongWritable(1));
		}
	}
	
}

WCReducer.java

package zengmg.hadoop.mr.wordcount;

import java.io.IOException;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class WCReducer extends Reducer {

	// 框架在map处理完成之后,将map输送过来的kv对缓存起来,进行分组,然后传递一个组,
	//每个key一组,每组调用一次reduce方法进行统计
	// 举例最后的kv对:,循环统计values就可以了
	@Override
	protected void reduce(Text key, Iterable values,
			Reducer.Context context) throws IOException, InterruptedException {
		
		long count=0;
		for(LongWritable value:values){
			count+=value.get();
		}
		
		context.write(key, new LongWritable(count));
	}
}

WCRunner.java

package zengmg.hadoop.mr.wordcount;

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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/*
 * 用来描述一个特定的作业
 * 在hadoop集群里有很多作业job,需要指定这个业务逻辑作业使用哪个类作为逻辑处理的map,哪个作为reduce
 * 还可以指定该作业要处理的数据所在的路径
 * 还可以指定该作业输出的结果放到哪个路径
 * .....
 * 该程序会打包成jar,放到hadoop的集群里跑
 */
public class WCRunner {
	
	public static void main(String[] args) throws Exception {
		
		Configuration conf=new Configuration();
		Job wcJob= Job.getInstance(conf);
		
		//设置这个job所用的mapper类和reducer类所在的jar包
		//必须指定,在hadoop里会有很多的jar包作业
		wcJob.setJarByClass(WCRunner.class);
		
		//设置job使用的mapper类和reducer类
		wcJob.setMapperClass(WCMapper.class);
		wcJob.setReducerClass(WCReducer.class);
		
		/*
		 * 设置reducer输出数据KV类型
		 * 先设置reducer的原因是:没有设置reducer输出数据kv的类型的api,
		 * setOutputKeyClass和setOutputValueClass会把reducer和mapper都设置了。
		 * 如果reducer和mapper的输出类型一样,就用这条就够了,无需setMapOutput...
		 */
		wcJob.setOutputKeyClass(Text.class);
		wcJob.setOutputValueClass(LongWritable.class);
		
		//设置mapper输出数据kv类型
		//wcJob.setMapOutputKeyClass(Text.class);
		//wcJob.setMapOutputValueClass(LongWritable.class);
		
		//指定要处理的输入数据存放路径
		FileInputFormat.setInputPaths(wcJob, new Path("hdfs://hello110:9000/wc/srcdata/"));
		//指定处理结果的输出数据存放路径
		FileOutputFormat.setOutputPath(wcJob, new Path("hdfs://hello110:9000/wc/output/"));
		
		//将job提交给集群运行,true:打印运行日志
		wcJob.waitForCompletion(true);
		
	}
}

导出jar包,放到hadoop集群下运行。

1、启动hadoop,start-all.sh

2、创建输出文件

hadoop fs -mkdir /wc/srcdata/

3、编辑要统计的文件

vi word.log

4、上传word.log到 hdfs的 /wc/srcdata  文件夹下。

hadoop fs -put word.log /wc/srcdata

5、运行jar包

hadoop jar wcount.jar zengmg.hadoop.mr.wordcount.WCRunner

提示 successful,表明成功

6、进入输出文件查看

 hadoop fs -ls /wc/output

 hadoop fs -cat /wc/output/part-r-00000


结果:

boy     1
girl    1
hello   4
mimi    1
world   1

按abc字母顺序表排序的







你可能感兴趣的:(大数据,hadoop)