5.2 Hadoop 序列化
5.2.1 序列化概述
-
什么是序列化
序列化就是 把内存中的对象,转换成字节序列 (或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输
反序列化就是将收到字节序列(或其他数据传输协议)或者是 磁盘的持久化数据,转换成内存中的对象
-
为什么要序列化
- 一般来说,“活的”对象只生存在内存里,关机断电就没有了
- “活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机,mapTask和reduceTask有可能放在不同的主机上运行,将对象序列化存储,可以保证数据从mapTask到不同主机的reduceTask
-
为什么不用 Java的序列化
-
Java的序列化是一个重量级序列化框架( Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息, Header,继承体系等),不便于在网络中高效传输
-
所以Hadoop自己开发了一套序列化机制( Writable)
-
-
Hadoop序列化特点:
- 紧凑 高效使用存储空间
- 快速 读写数据的额外开销小
- 互操作 支持多语言的交互
5.2.2 bean对象实现序列化
- 企业开发中往往常用的基本序列化类型不能满足所有需求 如想在 Hadoop框架内部传递一个 bean对象, 那么 该对象就需要实现序列化接口
- 实现bean对象序列化步骤:
- 必须 实现 Writable接口
- 反序列化时,需要反射调用空参构造函数,所以必须有空参构造
- 重写序列化方法
- 重写反序列化方法
- 反序列化的顺序和序列化的顺序完全一致
- 要想把结果显示在文件中,需要重写toString(),可用"\t"分开,方便后续用
- 如果需要将自定义的bean 放在key 中传输,则还需要实现Comparable 接口,因为MapReduce 框中的Shuffle 过程要求对key 必须能排序(详见后面排序案例)
5.2.3 序列化案例
-
需求
-
输入数据格式
Id 手机号码 网络ip 上行流量 下行流量 网络状态码 1 13736230513 192.196.100.1 www.atguigu.com 2481 24681 200 2 13846544121 192.196.100.2 264 0 200 3 13956435636 192.196.100.3 132 1512 200 4 13966251146 192.168.100.1 240 0 404
-
期望输出数据格式
(将上行流量、下行流量、总流量封装在bean中,bean对象如果想要传输,就必须实现序列化接口)
电话号 上行流量 下行流量 总流量 13470253144 180 180 360 13509468723 7335 110349 117684 13560439638 918 4938 5856
-
-
分析:
-
map阶段
- 读取一行数据,切分字段
- 抽取电话号、上行流量、下行流量
- 以手机号为key,bean作为value进行map的输出
-
reduce阶段
累加上行流量、下行流量
-
-
代码:
-
FlowBean
/* * 1. 定义类实现writable接口 * 2. 重写序列化和反序列化方法 * 3. 重写空参构造 * 4. toString方法用于打印输出 * */ public class FlowBean implements Writable { private long upFlow; //上行流量 private long downFlow; //下行流量 private long sumFlow; //总流量 //空参构造 public FlowBean() { } //序列化方法 @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(); this.downFlow = dataInput.readLong(); this.sumFlow = dataInput.readLong(); } //重写toString方法 @Override public String toString() { return upFlow +"\t"+ downFlow +"\t"+ sumFlow; } public long getUpFlow() { return upFlow; } public long getDownFlow() { return downFlow; } public long getSumFlow() { return sumFlow; } public void setUpFlow(long upFlow) { this.upFlow = upFlow; } public void setDownFlow(long downFlow) { this.downFlow = downFlow; } public void setSumFlow(long sumFlow) { this.sumFlow = sumFlow; } public void setSumFlow() { this.sumFlow = this.upFlow + this.downFlow; } }
-
mapper类
public class FlowMapper extends Mapper<LongWritable,Text,Text,FlowBean> { private Text outKey = new Text(); private FlowBean outValue = new FlowBean(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //1. 获取一行信息 String line = value.toString(); //2. 切割一行 String[] split = line.split("\t"); //3. 抓取想要的数据 //数据每行构造有残缺,但从后取角标一致 String phone = split[1]; String upflow = split[split.length - 3]; String downflow = split[split.length - 2]; //4. 封装,以便mapper输出 outKey.set(phone); outValue.setUpFlow(Long.parseLong(upflow)); outValue.setDownFlow(Long.parseLong(downflow)); outValue.setSumFlow(); //5. 写出 context.write(outKey,outValue); } }
-
reducer类
public class FlowReducer extends Reducer<Text,FlowBean,Text,FlowBean> { private FlowBean outValue = new FlowBean(); @Override protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException { //1. 遍历集合累加值 long totalUp = 0; long totalDown = 0; //相同key的输入,会进入循环 for (FlowBean value : values) { totalUp += value.getUpFlow(); totalDown += value.getDownFlow(); } //2. 封装outKey,outValue outValue.setUpFlow(totalUp); outValue.setDownFlow(totalDown); outValue.setSumFlow(); context.write(key,outValue); } }
-
driver类
public class FlowDriver { public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException { Configuration config = new Configuration(); Job job = Job.getInstance(config); 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("D:/FlowTest/input")); FileOutputFormat.setOutputPath(job,new Path("D:/FlowTest/output")); boolean result = job.waitForCompletion(true); System.exit(result ? 0 : 1); } }
-