1)需求:
统计每一个手机号耗费的总上行流量、下行流量、总流量
2)数据准备
输入数据格式:
数据格式:时间戳、电话号码、基站的物理地址、访问网址的ip、网站域名、数据包、接包数、上行/传流量、下行/载流量、响应码 |
输出数据格式
13560436666 1116 954 2070 手机号码 上行流量 下行流量 总流量 |
3)分析
基本思路:
Map阶段:
(1)读取一行数据,切分字段
(2)抽取手机号、上行流量、下行流量
(3)以手机号为key,bean对象为value输出,即context.write(手机号,bean);
Reduce阶段:
(1)累加上行流量和下行流量得到总流量。
(2)实现自定义的bean来封装流量信息,并将bean作为map输出的key来传输
(3)MR程序在处理数据的过程中会对数据排序(map输出的kv对传输到reduce之前,会排序),排序的依据是map输出的key
所以,我们如果要实现自己需要的排序规则,则可以考虑将排序因素放到key中,让key实现接口:WritableComparable。
然后重写key的compareTo方法。
4)编写mapreduce程序
(1)编写流量统计的bean对象
package bigdata.b12.hadoop721;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@Getter
@Setter
@AllArgsConstructor
/**
* DataOutput接口提供将数据从任何Java基本类型转换为一系列字节,
* 并将这些字节写入二进制流。还有一种将String转换为modified UTF-8格式并编写结果字节系列的功能。
* 对于写入字节的这个接口中的所有方法,一般来说,如果一个字节不能由于任何原因写入,则抛出一个IOException 。
*/
//1、实现writable接口
/**
* 在hadoop2.71.中,Writable共有6个子接口:
*
* Counter,CounterGroup,CounterGroupBase<T>,InputSplit,InputSplitWithLocationInfo,WritableComparable<T>
*
* 在org.apache.hadoop.io中有一个:WritableComparable<T>
* WritableComparable,顾名思义,它提供类型比较的能力,WritableComparables 能够通过 Comparators进行互相比较。主要是针对MapReduce而设计的,中间有个排序很重要。在 Hadoop Map-Reduce framework中,任何key值类型都要实现这个接口。WritableComparable是可序列化的,所以,同样实现了readFiels()和write()这两个序列化和反序列化方法,它多了一个比较的能力,所以实现compareTo()方法,该方法即是比较和排序规则的实现,因此,使用该类的实现类MR中的key值就既能可序列化又是可比较的。当然了,如果仅是做为值使用的话,仅实现Writable接口即可,接下来也要重点介绍WritableComparable这个接口。
* ————————————————
*
* 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
*
* 原文链接:https://blog.csdn.net/hadoop_/article/details/49276623
*/
public class FlowBean implements Writable {
private long upflow;//上行流量
private long downflow;//下行流量
private long sumflow;//总流量
//空构造器
public FlowBean() {
}
public FlowBean(long upflow, long downflow) {
this.upflow = upflow;
this.downflow = downflow;
}
public long getUpflow() {
return upflow;
}
public void setUpflow(long upflow) {
this.upflow = upflow;
}
public long getDownflow() {
return downflow;
}
public void setDownflow(long downflow) {
this.downflow = downflow;
}
public long getSumflow() {
return sumflow;
}
public void setSumflow(long sumflow) {
this.sumflow = sumflow;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
//writeLong(long v)
//将 long值(由八个字节组成)写入输出流。
dataOutput.writeLong(this.upflow);
dataOutput.writeLong(this.downflow);
dataOutput.writeLong(this.sumflow);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
//DataInput接口提供从二进制流读取字节并从其中重建任何Java原语类型的数据。
// 还有,为了重建设施String从数据modified UTF-8格式。
this.upflow = dataInput.readLong();
this.downflow = dataInput.readLong();
this.sumflow = dataInput.readLong();
}
@Override
public String toString() {
return "FlowBean{" +
"upflow=" + upflow +
", downflow=" + downflow +
", sumflow=" + sumflow +
'}';
}
}
(2)编写mapper
package bigdata.b12.hadoop721;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class MapWritable extends Mapper<LongWritable, Text,Text,FlowBean> {
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, FlowBean>.Context context) throws IOException, InterruptedException {
//获取数据
String valueString = value.toString();
//切分数据
String[] valstrsplit = valueString.split("\t");
//获取上传流量
long upflow = Long.parseLong(
valstrsplit[valstrsplit.length - 3]
);
long downflow = Long.parseLong(
valstrsplit[valstrsplit.length - 2]
);
//输出
context.write(new Text(valstrsplit[1]),new FlowBean(upflow,downflow));
}
}
(3)编写reducer
package bigdata.b12.hadoop721;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class ReduceWritable extends Reducer<Text, FlowBean, Text, FlowBean> {
@Override
protected void reduce(Text key, Iterable<FlowBean> values, Reducer<Text, FlowBean, Text, FlowBean>.Context context) throws IOException, InterruptedException {
//定义两个计数器,计算每个用户的上传流量和,与下载流量和
long sum_upflow = 0;
long sum_downflow = 0;
//累加流量和
for (FlowBean sumflow:values){
sum_upflow+=sumflow.getUpflow();
sum_downflow+=sumflow.getDownflow();
}
//输出
context.write(key,new FlowBean(sum_upflow,sum_downflow));
}
}
(4)编写驱动
package bigdata.b12.hadoop721;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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 MainWritable {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
args = new String[]{"D:\\test\\phone_data.txt","D:\\test\\writable1"};
//1、获取job信息
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//2、加载jar包
job.setJarByClass(MainWritable.class);
//3、关联map和reduce
job.setMapperClass(MapWritable.class);
job.setReducerClass(ReduceWritable.class);
//4、设置最终输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(FlowBean.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FlowBean.class);
//5、设置输入和输出路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//6、提交
job.waitForCompletion(true);
}
}