1:序列化概述
1:序列化就是内存中的对象转换成字节序列,反序列化就是字节序列转换成内存中的对象。
2:原因:因为把一个虚拟机的内存中的对象数据传给另一台虚拟机内存,没办法传。
3:不用Java的序列化原因: 因为Java的一个对象被序列化后,会覆盖很多额外的信息(各种校验信息、头、继承体系),不便于在网络中进行高效传输。
4:特点:结构紧凑,高效使用存储空间。快速,读写数据的额外开销小。互操作性:支持多语言的交互。加入Hadoop102用Java语言编写,Hadoop103用C语言也可完成反序列化。
2:案例
1、# 需求:统计每一个手机号耗费的总上行流量、总下行流量、总流量
2、# 输入的数据文件是phone_data.txt
# 输入的数据格式为:
7 11012013044 110.120.130.44 1116 954 200
id 手机号码 网络ip 上行流量 下行流量 网络状态码
3、#期望输出数据格式
11012013044 1116 954 2070
手机号码 上行流量 下行流量 总流量
4、Map阶段
1、读取一行数据、切分字段(\t空格都可以切割)
7 11012013044 110.120.130.44 1116 954 200
2、因为切割完是一个数组,可抽取手机号、上行流量、下行流量
11012013044 1116 954
手机号码 上行流量 下行流量
3、以手机号为key,bean对象为value输出,即context.write(手机号,bean)
4、bean对象要想能够传输,必须实现序列化接口
5、Reduce阶段
1、累加上行流量和下行流量得到总流量
11012013044 1116 + 954 = 2070
手机号码 上行流量 下行流量 总流量
3:编码
1:实体类
//1 继承 Writable 接口
public class FlowBean implements Writable {
private long upFlow; //上行流量
private long downFlow; //下行流量
private long sumFlow; //总流量
//2 提供无参构造
public FlowBean() {
}
//3 提供三个参数的 getter 和 setter 方法,三个参数都要进行,这里就列举一个,后续还有重写 toString方法,这里也不写了
public long getUpFlow() {
return upFlow;
}
public void setUpFlow(long upFlow) {
this.upFlow = upFlow;
}
public void setSumFlow() {//这个比较特殊,因为最后要计算
this.sumFlow = this.upFlow + this.downFlow;
}
//4 实现序列化和反序列化方法,注意顺序一定要保持一致
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeLong(upFlow);
dataOutput.writeLong(downFlow);
dataOutput.writeLong(sumFlow);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.upFlow = dataInput.readLong();//这个数据顺序和上面write方法里面对应
this.downFlow = dataInput.readLong();
this.sumFlow = dataInput.readLong();
}
2:Mapper类
public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean> {
private Text outK = new Text();
private FlowBean outV = new FlowBean();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1 获取一行数据,转成字符串 1 11012013044 110.120.130.44 1116 954 200
String line = value.toString();
//2 切割数据
//1,11012013044,110.120.130.44,1116,954,200
String[] split = line.split("\t");
//3 抓取我们需要的数据:手机号,上行流量,下行流量
String phone = split[1];
String up = split[split.length - 3];
String down = split[split.length - 2];
//4 封装 outK outV
outK.set(phone);
outV.setUpFlow(Long.parseLong(up));
outV.setDownFlow(Long.parseLong(down));
outV.setSumFlow();//这个是上面那个单独设置的无参的set方法
//5 写出 outK outV
context.write(outK, outV);
3:Reducer类
public class FlowReducer extends Reducer<Text, FlowBean, Text, FlowBean> {
private FlowBean outV = new FlowBean();
@Override
protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
//key为手机号,values是相同手机号对应的FlowBean
//因为一个手机号可能对应多个上行和下行,所以有以下的参数值
long totalUp = 0;
long totalDown = 0;
//1 遍历 values,将其中的上行流量,下行流量分别累加
for (FlowBean flowBean : values) {
totalUp += flowBean.getUpFlow();
totalDown += flowBean.getDownFlow();
}
//2 封装 outKV
outV.setUpFlow(totalUp);
outV.setDownFlow(totalDown);
outV.setSumFlow();
//3 写出 outK outV
context.write(key,outV);
}
}
4:Driver类
public class FlowDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//1 获取 job 对象
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//2 关联本 Driver 类
job.setJarByClass(FlowDriver.class);
//3 关联 Mapper 和 Reducer
job.setMapperClass(FlowMapper.class);
job.setReducerClass(FlowReducer.class);
//4 设置 Map 端输出 KV 类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(FlowBean.class);
//5 设置程序最终输出的 KV 类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FlowBean.class);
//6 设置程序的输入输出路径
FileInputFormat.setInputPaths(job, new Path("D:\\inputflow"));
FileOutputFormat.setOutputPath(job, new Path("D:\\flowoutput"));
//7 提交 Job
boolean b = job.waitForCompletion(true);
System.exit(b ? 0 : 1);
}
本文介绍了一个基于Hadoop的流量统计案例,通过自定义序列化方式提高数据处理效率。案例详细讲解了如何利用MapReduce计算每个手机号的总上行流量、总下行流量及总流量。
943

被折叠的 条评论
为什么被折叠?



