错误:
原题数据文件和题目要求
链接:https://pan.baidu.com/s/1FKcCVbwv30Q7roxZCJX8dw
提取码:6zja
题目要求:
使用MR求每个浏览器的数量,过滤掉状态码大于400的数据,或者状态码为空的数据,要求用对象封装数据,输出浏览器和数量
数据说明:
data[0] 用户ip,0
data[1] 客户端用户名
data[3]请求时间
data[5] 请求方法
data[6] 请求页面
data[7] http协议信息
data[8] 返回的状态码
data[9] 发送的页面字节数
data[10] 从什么页面跳转进来
date[11] 浏览器
自定义Bean对象代码如下:
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
*
* 自定义对象只对用到的数据进行了封装
*/
public class BrowserBean implements Writable {
//状态码
private Integer status;
//浏览器名称
private String browser;
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(status);
out.writeUTF(browser);
}
@Override
public void readFields(DataInput in) throws IOException {
this.status = in.readInt();
this.browser = in.readUTF();
}
@Override
public String toString() {
return "BrowserBean{" +
"status=" + status +
", browser='" + browser + '\'' +
'}';
}
public void setStatus(Integer status) {
this.status = status;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public BrowserBean() {
}
public BrowserBean(Integer status, String browser) {
this.status = status;
this.browser = browser;
}
public Integer getStatus() {
return status;
}
}
Mapper端代码
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class BrowserMapper extends Mapper<LongWritable, Text,BrowserBean,LongWritable> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//把value的值转化为字符串
String s = value.toString();
//清除空行
if (!s.isEmpty()) {
//对数据进行切分
String[] split = s.split(" ");
//判断状态码是否为空
boolean empty = split[8].isEmpty();
//如果不为空执行以下步骤
if (!empty) {
int i = Integer.parseInt(split[8]);
//判断状态码是否小于等于400
if (i <= 400) {
//如果状态码小于等于400就把对象和 1 输出
if (split.length > 11) {
//如果浏览器名不为空 或者不为无效数据 "-" 就执行
boolean b = !split[11].isEmpty() && !split[11].equals("\"-\"");
if (b){
//把数据封装到对象
BrowserBean browserBean = new BrowserBean(i, split[11]);
//输出结果
context.write(browserBean, new LongWritable(1));
}
}
}
}
}
}
}
Reducer 端代码
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class BrowserReducer extends Reducer<BrowserBean, LongWritable,Text, LongWritable> {
@Override
protected void reduce(BrowserBean key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
//定义计数器
long count =0 ;
for (LongWritable value : values) {
//计算总和
count += value.get();
//获取浏览器名
String browser = key.getBrowser();
//输出浏览器名 和 数量
context.write(new Text(browser), new LongWritable(count));
}
}
}
Driver(Runner)端代码
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.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
public class BrowserDriver extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
//示例一个job
Configuration c = new Configuration();
Job j = Job.getInstance(c, "BrowserDriver");
//设置map类和reduce类
j.setMapperClass(BrowserMapper.class);
j.setReducerClass(BrowserReducer.class);
//设置输出类 和 读取类
j.setOutputFormatClass(TextOutputFormat.class);
j.setInputFormatClass(TextInputFormat.class);
//设置mapper 的输出类型
j.setMapOutputKeyClass(BrowserBean.class);
j.setMapOutputValueClass(LongWritable.class);
//设置reducer 的输出类型
j.setOutputKeyClass(Text.class);
j.setOutputValueClass(LongWritable.class);
//设置读取 和输出路径
TextInputFormat.addInputPath(j, new Path("C:\\Users\\dell\\Desktop\\练习题\\access.log"));
TextOutputFormat.setOutputPath(j, new Path("C:\\Users\\dell\\Desktop\\test1"));
//获取执行之后的结果
boolean b = j.waitForCompletion(true);
//成功则 返回 0 , 失败返回 1
return b ? 0 : 1;
}
public static void main(String[] args) throws Exception {
//进行方法调用
ToolRunner.run(new BrowserDriver(), args);
}
}
报错分析:
经过排查一开始以为是自定义Bean对象的问题,在Bean对象中进行了反复的检查发现Bean对象中并没有出现任何的问题,于是进行了进一步的排查,在调用Bean对象的地方进行修改,终于发现是Mapper端输出值的问题.
报错原因:
哈希算法无法对只实现了Writable接口的自定义Bean 对象进行运算,导致partition阶段分区报错
解决方法:
方法一:修改mapper 输出的key值
方法二(推荐使用):如果要把mapper输出的类型key为自定义对象必须实现 WritableComparable<T>
接口
修改之后代码如下:
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
*
* 自定义对象只对用到的数据进行了封装
*/
public class BrowserBean implements WritableComparable<BrowserBean> {
//状态码
private Integer status;
//浏览器名称
private String browser;
@Override
public int compareTo(BrowserBean o) {
return 0;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(status);
out.writeUTF(browser);
}
@Override
public void readFields(DataInput in) throws IOException {
this.status = in.readInt();
this.browser = in.readUTF();
}
@Override
public String toString() {
return "BrowserBean{" +
"status=" + status +
", browser='" + browser + '\'' +
'}';
}
public void setStatus(Integer status) {
this.status = status;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public BrowserBean() {
}
public BrowserBean(Integer status, String browser) {
this.status = status;
this.browser = browser;
}
public Integer getStatus() {
return status;
}
}