对worldcount程序分析
1:整个wordcount分为3个阶段
mapper
reducer
driver
2:每个阶段的作用
mapper:对数据进行打散<hello,1><mimi,1>
reducer:对数据进行聚合<hello,1+1+1>
driver:提交任务
3:详解
mapper阶段:
将数据转换成String
对数据进行切分处理
把每个单词后加1
输出到reducer阶段
reducer阶段:
根据key进行聚合
输出key出现的总次数
driver阶段:
创建任务
关联使用的Mapper/Reducer类
指定Mapper输出数据的kv类型
指定reducer输出的数据的kv类型
指定数据的输入与输出路径
提交
#############################################
创建wordCountReducer 类:
package com.zhang_bigdata01;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* Reducer阶段接受的是Mapper输出的数据
* Mapper的输出是Reducer的输入
*
* KeyIn:Mapper输出Key的类型
* ValueIN:Mapper输出Value的类型
*
* Reducer端输出的数据类型,<hello,199>
* KeyOut:Text
* ValueOut:IntWritable
*
*/
public class wordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable>{
//Key ->单词 Value ->次数
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
//1:记录出现的次数
int sum = 0;
for (IntWritable v:values) {
sum += v.get();
}
//2:累加求和输出
context.write(key,new IntWritable(sum));
}
}
------------------------------------
创建wordCountMap 类:
package com.zhang_bigdata01;
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;
/**
* 数据的输入与输出以Key Value进行传输
* KeyIN:LongWritable(long)数据的起始偏移量
* ValueIN:具体数据 Text
*
* mapper需要把数据传递到reduce阶段<hello,1>
* KeyOut: 单词Text
* ValueOut: 出现的次数,IntWritable
*/
public class wordCountMap extends Mapper<LongWritable, Text,Text, IntWritable> {
//d对数据进行切分
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1:接入数据
String line = value.toString();
//2:对数据进行切分,以空格进行切分
String[] words = line.split(" ");
//3:写出以<hello,1>格式
for (String w:words) {
//写出reduce端
context.write(new Text(w),new IntWritable(1));
}
创建WorldCountCombine类,来优化
package 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 WorldCountCombine extends Reducer<Text, IntWritable,Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
//初始化计数器
int sum = 0;
//开始计数
for (IntWritable value : values) {
sum += value.get();
}
//输出
context.write(key,new IntWritable(sum));
}
}
-------------------------------
创建wordCountDriver 类:
package com.zhang_bigdata01;
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;
import java.io.IOException;
public class wordCountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//设置起始时间
long starttime = System.currentTimeMillis();
//这里是放在本地测试输入输出路径,注意输出目录需要不存在,也可打包上传hadoop后测试
args = new String[]{"E:\\bigdata_code\\wcword.txt","E:\\bigdata_code\\out"};
//1:创建job任务
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//2:指定jar包位置
job.setJarByClass(wordCountDriver.class);
//3:关联使用Mapper类,设置Mapper阶段数据输出类型
job.setMapperClass(wordCountMap.class);
//4:关联使用Reducer类
job.setReducerClass(wordCountReducer.class);
//5:设置Mapper阶段输出的数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//6:设置Reducer阶段输出的数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置预合并combine
job.setCombinerClass(WorldCountCombine.class);
//7:设置数据输入的路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
//8:设置数据输出的路径
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//9:提交任务
boolean rs = job.waitForCompletion(true);
System.exit(rs?0:1);
//设置结束时间,统计程序的执行时间
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)/1000);
}
}
这里统计的是/var/log/messages,统计结果如下:
5568
"centos" 22
"d:group:adm:r-x,d:group:wheel:r-x": 22
"group:adm:r-x,group:wheel:r-x": 22
"pci=nocrs" 11
"plymouth 1
#011Offload 22
#011RCU 11
#011chunk_size: 11
#011lose 11
#011num_reg: 11
---------------------------------------------------
也可以在hadoop服务器上统计,结果相同
hadoop jar zhang-1.0-SNAPSHOT.jar com.zhang_bigdata01.WordCountMain /opt/hadoop_data/messages /opt/hadoop_data/out1
---------------------------------------------------
在IDEA中测试是报错如下:
Exception in thread"main"java.lang.UnsatisfiedLinkError:org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z
原因是:C:\Windows\System32下缺少hadoop.dll
解决方法:
将hadoop的bin下的hadoop.dll再往C:\Windows\System32下拷贝一份重启eclipse或者idea即可解决。
如果经历以上步骤之后还是第三个错误。
分析错误:错误指向的是hadoop的源码NativeIO.java的606行代码。
Windows的唯一方法用于检查当前进程的请求,在给定路径的访问权限,所以我们可以给予能访问的权限,可以在当前工程中临时先修改源代码,return true 时允许访问。
解决错误:
1.鼠标点击错误中的(NativeIO.java:606)会跳转到源码类中。
2.查看NativeIO.java的包package路径,在当前工程建立相同的package。
3.将源码NativeIO.java拷贝到步骤2建立的工程中。
4.找到错误提示的代码处,606行进行如下修改,即可解决。
//return access0(path, desiredAccess.accessRight());
return true;