案例实操wordcount案例,统计单词出现次数
编码先导入hadoop依赖包,下面这个路径里有详细操作步骤
https://blog.csdn.net/kxj19980524/article/details/89043569
mapper阶段
Mapper<LongWritable, Text, Text, IntWritable>
mapper后面的这四个参数,就是输入key类型输入value类型,输出key类型,输出value类型
看上图,读取hello.txt数据的时候它是一行一行读的,key就是行号,value就是这一行的字符串,对照上面hadoop与java对应的数据类型,long对应的是LongWritable,String对应的text所以第一个参数是LongWritable,第二个参数为Text,看下面代码,mapper里做的工作是把每行的数据按空格进行分开,然后输出到缓冲区,没分出一个单词就输出一次,输出的key就是单词名,value就是1.有几个重复的单词就在缓冲区value的数组里加个1,可以把value看成是一个数组.
package com.buba.mapreduce.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* 接口参数:
* 第一个参数 KEYIN:输入数据的key 文件的行号
* 第二个参数 VALUEIN:每行的输入数据
*
* 第三个参数 KEYOUT:输出数据的key
* 第四个参数 VALUEOUT:输出数据的value类型
*/
public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
//hello world
//hadoop spark
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1.获取这一行数据
String line = value.toString();
//2.获取每一个单词
String[] words = line.split(" ");
for(String word : words){
System.out.println(word);
//3.输出每一个单词
context.write(new Text(word),new IntWritable(1));
}
}
}
在源码中可以看到,每次写出一条数据的时候都会在这判断是否有下一行,然后再进map方法
reduce阶段
reducer后面的四个参数跟上面同理,整个mapper阶段执行完后才执行reducer阶段.这里面的逻辑就是把mapper阶段拆开的单词数量进行累加然后输出出去.
package com.buba.mapreduce.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordcountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
//1.统计所有单词个数
int count = 0;
for(IntWritable value:values){
System.out.println(key);
System.out.println(value);
count += value.get();
}
//2.输出所有单词个数
context.write(key,new IntWritable(count));
}
}
driver阶段
就是设置一些job的参数,这个mapreduce的各个参数.
package com.buba.mapreduce.wordcount;
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 WordcountDriver {
public static void main(String[] args) throws Exception{
//1.获取job对象信息 Configuration这个对面默认从本地获取,直接new对象就行不用添加参数
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
//2.设置加载jar位置
job.setJarByClass(WordcountDriver.class);
//3.设置mapper和reducer的class类
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordcountReducer.class);
//4.设置输出mapper的数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//5.设置最终数据输出的类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6.设置输入数据和输出数据路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//7.提交
boolean result = job.waitForCompletion(true);
//如果成功返回0,失败返回1
System.exit(result?0:1);
}
}
另一种写法
package com.buba.mapreduce.wordcount;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
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.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 WordcountDriver extends Configured implements Tool{
public static void main(String[] args) throws Exception {
WordcountDriver lc = new WordcountDriver();
int status = ToolRunner.run(lc.getConf(), lc, args);
System.exit(status);
}
public int run(String[] args) throws Exception {
//1.获取job对象信息 Configuration这个对面默认从本地获取,直接new对象就行不用添加参数
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
//2.设置加载jar位置
job.setJarByClass(WordcountDriver.class);
//3.设置mapper和reducer的class类
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordcountReducer.class);
//4.设置输出mapper的数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//5.设置最终数据输出的类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6.设置输入数据和输出数据路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//7.提交
boolean result = job.waitForCompletion(true);
return result ? 0 : 1;
}
}
进行测试
测试1,在window测试,需要安装hadoop环境变量.把main方法添加两个参数,因为driver代码里调用了args数组里的两个值.这两个参数第一个是输入参数路径,就是源数据文件,第二个参数是mapreduce执行完后生成的文件路径地址,注意,这个路径不能存在,是执行完后自动生成的,如果这个文件本来就存在会报错.
放入一个log4j,要不然出不来日志
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n