1. wordcount题目描述
wordcount:统计2个文件中单词出现的总次数
$ cat 1.txt
hello hi love me brief
outlook me hello china brief
hi love beijing hi hi
$ cat 2.txt
hello hi love me brief
outlook me hello china brief
hi love beijing hi hi
facebook china beijing china beijing
2. wordcount编程
WordCountMapper.java
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;
/*
* 统计每一个小文件的
* 1)继承Mapper
* 2)重写map
*
* Mapper需要的4个泛型:
* 框架读取完文件给你的内容
* KEYIN, 输入的键的类型 这里指的是每一行的起始字节偏移量
* VALUEIN, 输入的值的类型 这里指的是一行的内容 String
*
* 输出
* KEYOUT, 输出的建的类型 这里指的是每一个单词 String
* VALUEOUT 输出的值的类型 这里指的是单词的次数 int
*
* 因为map数据要经过网络传输到reduce那,所有的数据类型必须具备序列化和反序列化的能力
* 序列化:字符串---》二进制
* 反序列化:二进制---》字符串
* java中的序列化 反序列化接口 Serializable
* 但是hadoop弃用了java中的
* java中的序列化反序列化 太重 过于繁杂
* Long java序列化反序列化将类结构一并序列化反序列化
* hadoop中重新定义了一套序列化 反序列化的接口 Writable 轻
* 对于我们常用的一些类型 帮我们实现了对应的类型:
* long---->LongWritable
* int------>IntWritable
* byte---->ByteWritable
* double---->DoubleWritable
* Null---->NullWritable
* String---->Text
*/
/*
* 在map端 获取每一行的内容 切分 发送
*/
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
/*
* LongWritable key:输入的每一行的偏移量 框架读取的
* Text value:输入的每一行的内容
* Context context:上下文对象 用于向reduce发送数据 读取框架给的东西
*
* 调用频率:一行调用一次
* 一个文本100行 这个方法就会调用100次
*/
@Override
protected void map(LongWritable key,
Text value,
Context context)
throws IOException, InterruptedException {
//获取每一行内容 转换为String
String line = value.toString();
//切分每一个单词 hello hi love me brief
String[] words = line.split("\t");
/*
* 如果要统计 只能统计一行的 并不能统计一个文件的
* 全部发送到reduce端进行统计
* key:单词
* value:1
*/
//循环遍历每一个单词 发送给reduce
for(String w:words){
Text k=new Text(w);
IntWritable v=new IntWritable(1);
//把单词 1发送reduce
context.write(k, v);
}
}
}
WordCountReducer.java
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
/*
* 统计分析
* 1.继承Reducer
* 2.重写reduce()
*
* Reducer中的四个泛型:
* 输入:从map过来的 对应的map的输出类型
* KEYIN, reduce的输入的key的类型 --map输出的key的类型 Text
* VALUEIN, reduce的输入的value的类型--map输出的value类型
*
* 输出:最终的结果输出
* KEYOUT, 输出的key类型 这里指的最终的单词 Text
* VALUEOUT 输出的value类型 这里指的是每一个单词的最终的词频 IntWritable
*/
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
/*map到reduce之间有一个shuffle的过程(洗牌/将map输出的数据进行打乱 重洗)
* 目前我们使用的是默认的 洗牌过程中有一步分组的
* 默认的分组 按照map输出的key进行分组的
*
* 这里map输出的key是单词 按照单词进行分组
* 会把单词相同的所有的数据分在一组
* hello,1 hello,1
* a,1 hello,1
* 分组:
* hello,1 hello,1 hello,1 组1
* a,1 组2
*
* Text key, 一组中的任意一个key hello
* Iterable<IntWritable> values, 一组中的相同的key对应的所有的value <1,1,1>
Context context 上下文对象 向上承接map 向下输出结果 hdfs/本地
这个函数的调用频率:一组调用一次 有多少组就调用几次
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
//循环遍历values求和即可
int sum=0;
for(IntWritable v:values){
//将v转为int类型 get hadoop--->java
sum+=v.get();
}
//写出
IntWritable rv=new IntWritable(sum);
context.write(key, rv);
}
}
Driver.java
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;
/*
* 提交运行mapreduce的程序的
*/
public class Driver {
/*
* String[] args 代表程序运行过程中接受的参数
* 按照参数的顺序放在数组的下标中
* 第一个参数 -- 数组的下标0的位置
*/
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//加载配置文件
/*
* 优先加载jar包中的 hdfs-default.xml mapred-default.xml ...
* src下的 -site.xml -default.xml
* 代码中的
* 最后加载的最终生效
*/
Configuration conf=new Configuration();
//启动一个job
/*
* job的概念 一个mapreduce程序 封装的map或reduce的相关的配置项
*/
//创建job对象
Job job=Job.getInstance(conf);
//封装上面的job 将自定义的mapper reducer类封装到job上
//指定当前job的入口 main入口
//参数class 类型 1)类名.class 2)对象。getClass 3)class.forname()
job.setJarByClass(Driver.class);
//指定job的对应的Mapper类
job.setMapperClass(WordCountMapper.class);
//指定job对应的reducer的类
job.setReducerClass(WordCountReducer.class);
//指定mapper的输出的key 和value类型
/*
* 泛型的作用范围:
* 编译时可以进行检查数据类型 运行时泛型自动擦除
*/
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定reducer输出的key value的类型
/*
* 这个方法设定的是输出的key value的类型
* 如果map指定了 这个指的就是reduce
* 如果map没有指定 这个指的是map和reduce的
* 当map reduce的输出的key value的类型一致的时候只需指定下面的就可以
*/
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//指定输入 需要处理的文件 添加输入路径
//参数1 job 参数2 输入路径
//FileInputFormat.addInputPath(job, new Path("hdfs://hdp01:9000/in"));
FileInputFormat.addInputPath(job, new Path(args[0]));
//指定输出路径 输出路径不能存在 否则报错
//FileOutputFormat.setOutputPath(job, new Path("/out_01"));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//提交job
//这个方法基本不用 不打印日志
//job.submit();
//可以打印日志的 参数代表是否打印运行的进程
job.waitForCompletion(true);
}
}
运行结果:
[hdp01@hdp01 jars]$ hadoop jar follow-1.0-SNAPSHOT.jar com.study.follow.wordcount.Driver /wcin /wcout
输出日志省略
[hdp01@hdp01 jars]$ hdfs dfs -cat /wcout/part-r-00000
beijing 4
brief 4
china 4
facebook 1
hello 4
hi 8
love 4
me 4
outlook 2