1 序列化概述
1.1 什么是序列化和反序列化
序列化
就是把内存中的对象,转换成字节序列
(或其他数据传输协议
)以便于存储到磁盘(持久化)
和网络传输
反序列化
就是将收到字节序列
(或其他数据传输协议
)或者是磁盘的持久化数据
,转换成内存中的对象
1.2 为什么要序列化
—般来说,
“活的”对象
只生存在内存
里,关机断电就没有了。而且“活的”对象只能由本地的进程
使用,不能被发送到网络上的另外一台计算机。然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。也就是将对象序列化
成字节序列
,然后就能被网络传输
或者是存储到磁盘中
,而不是存储在内存
中,在断电以后就会消失
1.3 为什么不用java序列化
hadoop的信息处理也需要经过序列化,因为数据需要在介质之间传输,但是
hadoop
的序列化是自定义
的,而不是使用Java的序列化
。因为Java
的序列化是一个重量级序列化框架Serializable
),一个对象被序列化后,会附带很多额外的信息(各种校验信息,Header
,继承体系等),不便于在网络中高效传输。所以,hadoop
自己开发了一套序列化机制(Writable
)。
1.4 hadoop序列化特点
- 紧凑 :高效使用存储空间。
- 快速 :读写数据的额外开销小。
- 可扩展 :随着通信协议的升级而可升级
- 互操作 :支持多语言的交互
2 实现hadoop的Writable接口
2.1 hadoop的基本序列化类型
hadoop
中数据类型是自定义的
,Java的对于每个基本类型的数在例如参数传递的场景都会经过序列化,但是hadoop
不使用Java
的序列化机制,所以就需要自定义自身的基本数据类型。以下是hadoop
的基本数据类型和Java
的对比
但是仅仅是基本数据类型的序列化是远远不够的,所以需要自行实现
Writable接口
来自定义对象的序列化
2.2 接口实现基本步骤
- 自定的类需要实现
Writable接口
- 提供
无参数构造器
(反序列化时会通过反射
的方式调用无参数构造器
来创建对象)重写 write
方法实现序列化的过程(自定义序列化的内容)- 重写
readFields
方法实现反序列化的过程
3 序列化案例实操
1、需求分析:统计每一个手机号耗费的总上行流量、下行流量、总流量
2、期望输出:
#手机号 上行流量 下行流量 总流量
13560436666 1126 954 2080
2、测试数据:
#ID 手机号 IP 上行流量 下行流量 网络状态码
1 13560436666 120.196.101.99 1126 954 200
2 13560436667 120.196.102.99 1136 954 200
3 13560436668 120.196.103.99 1146 954 200
4 13560436669 120.196.104.99 1156 954 200
5 13560436676 120.196.105.99 1166 954 200
6 13560436686 120.196.106.99 1176 954 200
7 13560436666 120.196.107.99 1126 954 200
8 13560436667 120.196.108.99 1136 954 200
9 13560436668 120.196.109.99 1146 954 200
3、实现思路:在map
阶段会将每一行数据按空格进行切分,切分出手机号后,将对应的上行流量以及下行流量处理到一个bean对象
,然后序列化输出。虽然是可以使用其他方法,但是这里仅就对序列化学习。对于简单的MapReduce程序
编写可以看这里:MapReduce学习2-1:以官方wordcount实例为例的MapReduce程序学习
对于
mapper程序
通过context.write
方法进行参数传递过程中会有一个写入磁盘的过程,所以需要序列化,对于hadoop
的基本数据类型是已经实现了序列化而无需自行处理,但是对于其他对象就需要自行实现Writable接口
4、创建FlowBean类
并实现Writable接口
package com.writable.maven;
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class FlowBean implements Writable {
// 上行流量、下行流量和总流量
private Long upFlow;
private Long downFlow;
private Long sumFlow;
FlowBean(){
}
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 void setSumFlow(){
this.sumFlow = this.upFlow + this.downFlow;
}
public Long getSumFlow() {
return sumFlow;
}
public void setSumFlow(Long sumFlow) {
this.sumFlow = sumFlow;
}
// 实现Writable接口必须实现该方法,用以将数据序列化输出,输出顺序为执行顺序以及类型也为设置的类型,如下
@Override
public void write(DataOutput out) throws IOException {
out.writeLong(this.upFlow);
out.writeLong(this.downFlow);
out.writeLong(this.sumFlow);
}
// 实现Writable接口必须实现该方法,用以将数据反序列化输出,执行顺序以及类型也为设置的类型与write方法相适应
@Override
public void readFields(DataInput in) throws IOException {
this.upFlow = in.readLong();
this.downFlow = in.readLong();
this.sumFlow = in.readLong();
}
@Override
public String toString() {
return this.upFlow + "\t" + this.downFlow + "\t" + this.sumFlow;
}
}
5、创建FlowMapper类
package com.writable.maven;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean> {
private Text outK = new Text();
private FlowBean flowBean = new FlowBean();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// super.map(key, value, context);
// 获取一行
String line = value.toString();
// 按空白分割一行
String[] parts = line.split("\\s+");
outK.set(parts[1]);
flowBean.setUpFlow(Long.parseLong(parts[parts.length-3]));
flowBean.setDownFlow(Long.parseLong(parts[parts.length-2]));
flowBean.setSumFlow();
context.write(outK, flowBean);
}
}
6、创建FlowReducer
类
package com.writable.maven;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class FlowReducer extends Reducer<Text, FlowBean,Text, FlowBean> {
private FlowBean flowBean = new FlowBean();
@Override
protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
// super.reduce(key, values, context);
Long sumUpFlow = 0L;
Long sumDownFlow = 0L;
for (FlowBean fb: values){
sumUpFlow += fb.getUpFlow();
sumDownFlow += fb.getDownFlow();
}
flowBean.setUpFlow(sumUpFlow);
flowBean.setDownFlow(sumDownFlow);
flowBean.setSumFlow();
context.write(key, flowBean);
}
}
7、创建FlowDriver
类
package com.writable.maven;
import org.apache.hadoop.conf.Configuration;
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.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class FlowDriver{
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
Job job = new Job(conf);
job.setJarByClass(FlowDriver.class);
job.setMapperClass(FlowMapper.class);
job.setReducerClass(FlowReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(FlowBean.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FlowBean.class);
// 这里仅做本地测试
FileInputFormat.setInputPaths(job, new Path("E:\\bigdata\\study\\test_files\\flowinput\\flow.txt"));
FileOutputFormat.setOutputPath(job, new Path("E:\\bigdata\\study\\test_files\\flowoutput"));
job.waitForCompletion(true);
}
}
上述代码不多做解释,如需详细了解可看这里:MapReduce学习2-1:以官方wordcount实例为例的MapReduce程序学习
8、结果