- 1 序列化概述
- 1.1 什么是序列化和反序列化
- 1.2 为什么要序列化
- 1.3 为什么不用java序列化
- 1.4 hadoop序列化特点
- 2 实现hadoop的Writable接口
- 2.1 hadoop的基本序列化类型
- 2.2 接口实现基本步骤
- 3 序列化案例实操
序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输
1.2 为什么要序列化反序列化就是将收到字节序列(或其他数据传输协议)或者是磁盘的持久化数据,转换成内存中的对象
1.3 为什么不用java序列化—般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。也就是将对象序列化成字节序列,然后就能被网络传输或者是存储到磁盘中,而不是存储在内存中,在断电以后就会消失
1.4 hadoop序列化特点hadoop的信息处理也需要经过序列化,因为数据需要在介质之间传输,但是hadoop的序列化是自定义的,而不是使用Java的序列化。因为Java的序列化是一个重量级序列化框架Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,Header,继承体系等),不便于在网络中高效传输。所以,hadoop自己开发了一套序列化机制(Writable)。
- 紧凑 :高效使用存储空间。
- 快速 :读写数据的额外开销小。
- 可扩展 :随着通信协议的升级而可升级
- 互操作 :支持多语言的交互
2.2 接口实现基本步骤hadoop中数据类型是自定义的,Java的对于每个基本类型的数在例如参数传递的场景都会经过序列化,但是hadoop不使用Java的序列化机制,所以就需要自定义自身的基本数据类型。以下是hadoop的基本数据类型和Java的对比
但是仅仅是基本数据类型的序列化是远远不够的,所以需要自行实现
Writable接口来自定义对象的序列化
- 自定的类需要实现Writable接口
- 提供无参数构造器(反序列化时会通过反射的方式调用无参数构造器来创建对象)重写
- write方法实现序列化的过程(自定义序列化的内容)
- 重写readFields方法实现反序列化的过程
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{ 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{ private FlowBean flowBean = new FlowBean(); @Override protected void reduce(Text key, Iterable 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、结果



