【Hadoop】5.2 Hadoop 序列化

5.2.1 序列化概述

  1. 什么是序列化

    序列化就是 把内存中的对象,转换成字节序列 (或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输

    反序列化就是将收到字节序列(或其他数据传输协议)或者是 磁盘的持久化数据,转换成内存中的对象

  2. 为什么要序列化

    1. 一般来说,“活的”对象只生存在内存里,关机断电就没有了
    2. “活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机,mapTask和reduceTask有可能放在不同的主机上运行,将对象序列化存储,可以保证数据从mapTask到不同主机的reduceTask
  3. 为什么不用 Java的序列化

    1. Java的序列化是一个重量级序列化框架( Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息, Header,继承体系等),不便于在网络中高效传输

    2. 所以Hadoop自己开发了一套序列化机制( Writable)

      在这里插入图片描述

  4. Hadoop序列化特点:

    1. 紧凑 高效使用存储空间
    2. 快速 读写数据的额外开销小
    3. 互操作 支持多语言的交互

5.2.2 bean对象实现序列化

  1. 企业开发中往往常用的基本序列化类型不能满足所有需求 如想在 Hadoop框架内部传递一个 bean对象, 那么 该对象就需要实现序列化接口
  2. 实现bean对象序列化步骤:
    1. 必须 实现 Writable接口
    2. 反序列化时,需要反射调用空参构造函数,所以必须有空参构造
    3. 重写序列化方法
    4. 重写反序列化方法
    5. 反序列化的顺序和序列化的顺序完全一致
    6. 要想把结果显示在文件中,需要重写toString(),可用"\t"分开,方便后续用
    7. 如果需要将自定义的bean 放在key 中传输,则还需要实现Comparable 接口,因为MapReduce 框中的Shuffle 过程要求对key 必须能排序(详见后面排序案例)

5.2.3 序列化案例

  1. 需求

    1. 输入数据格式

      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
      
    2. 期望输出数据格式

      (将上行流量、下行流量、总流量封装在bean中,bean对象如果想要传输,就必须实现序列化接口)

      电话号		上行流量 	下行流量	总流量
      13470253144	180	180	360
      13509468723	7335	110349	117684
      13560439638	918	4938	5856
      
  2. 分析:

    1. map阶段

      1. 读取一行数据,切分字段
      2. 抽取电话号、上行流量、下行流量
      3. 以手机号为key,bean作为value进行map的输出
    2. reduce阶段

      累加上行流量、下行流量

  3. 代码:

    1. 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;
          }
      }
      
    2. 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);
          }
      }
      
    3. 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);
          }
      }
      
    4. 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);
          }
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值