三-中上, 大数据基础架构Hadoop- Hadoop序列化概述和案例实操 hf

二, Hadoop序列化

2.1 序列化概述

[什么是序列化 ?]

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

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

简而言之就是, 序列化是把对象转化为可传输的字节序列, 反序列化是把可传输的字节序列转化为对象.

[为什么要序列化 ?]

  • 一般来说, "活的"对象只生存在内存中, 关机断电就没有了. 而且, "活的"对象只能由本地的进程使用, 不能发送到网络上的另外一台计算机. 然而序列化可以存储"活的"对象, 可以把"活的"对象发送到远程计算机.

[为什么不使用Java的序列化 ?]

  • Java的序列化是一个重量级序列化框架(Serializable), 一个对象被序列化后, 会附带很多额外的信息(各种校验信息, Header, 继承体系等), 不便于在网络中高效传输. 所有Hadoop自己开发了一套序列化机制(Writable).

Hadoop序列化特点:

  1. 紧凑: 高效使用存储空间(附加信息少, 比如相比Java序列化只需要简单的校验信息)
  2. 快速: 读写数据的额外开销小(紧凑,所有传输开销小)
  3. 互操作: 支持多语言的交互(这边Java序列化后, 接收方可用C,C++之类的反序列化)

2.2 自定义bean对象实现序列化接口(Writable)

首先我们看一下Writable接口的源码

在企业开发中, 往往常用的基本序列化类型不能满足所有需求, 比如在Hadoop框架内部传递一个bean对象(相当于实体类),那么该对象需要实现序列化接口.

what is the bean ?
what is the bean 下 ?

[具体实现 bean对象序列化的步骤如下: ]

  1. 必须实现Writable接口
  2. 重写序列化方法write(DataOutput dataOutput)
  3. 重写反序列化方法readFields(DataInput dataInput
  4. 反序列化时, 需要反射调用空参构造函数, 必须要有空参构造
  5. 注意反序列化的顺序和序列化的顺序完全一致
  6. 要想把结果显示在文件中(默认传输过来的是地址值), 需要重写toString(), 可用"\t"分开, 方便后续使用
  7. 如果需要将自定义的bean放在key中传输, 则还需要实现Comparable接口, 因为MapReduce框架中的Shuffle过程要求对key必须能排序.

注意: 对每一个属性序列化的顺序, 要跟反序列化的顺序一一对应,先序列化的属性先被反序列化.

2.3 序列化案例实操

[需求]

[需求分析]

[具体实现]

  1. 编写FlowBean类作为bean类(实体类), 其中包含了
    需要序列化的私有属性,
    公有的get和set方法,
    无参的构造方法,
    用于输出的toString(),
    以及bean类不可或缺的序列化方法write()和反序列化方法readFiles;

  2. 编写FlowMapper, 继承org.apache.hadoop.mapreduce.Mapper中的Mapper接口, 实现其Mapper方法, 在Mapper方法中,
    KEYIN- 偏移量, VALUEIN- 文本中的一行,
    KEYOUT- 手机号(有重复), VALUEOUT-bean类(包含需要序列化的属性, 上行, 下行, 总流量);

    map方法主要就是实现有效数据的提取和整理

  3. 编写FlowReducer, 继承org.apache.hadoop.mapreduce.Reducer中的Reducer接口, 实现其reduce方法, 在这个方法中,
    KEYIN- 手机号(有重复), VALUEIN-bean类(包含需要序列化的属性, 上行, 下行, 总流量); ,
    KEYOUT-手机号(无重复), VALUEOUT-bean类(对相同手机号的上行,下行, 总流量进行汇总)

    reduce方法主要完成对重复手机号的提取, 以及对重复手机号不同的上下行流量和总流量的汇总.

  • 具体代码如下:

FlowBean

package writable;

import org.apache.hadoop.io.Writable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/*** Bean类(实体类)
 * 1.实例化Writable接口
 * 2.空参构造
 * 3.get, set方法
 * 4.重写序列化方法
 * 5.重写反序列化方法
 */
public class FlowBean implements Writable {
    
    
    //自定义属性
    private long upFlow;
    private long downFlow;
    private long sumFlow;


    //getter setter
    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 long getSumFlow() {
    
    
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
    
    
        this.sumFlow = sumFlow;
    }

    public void setSumFlow(){
    
    
        this.sumFlow = this.upFlow + this.downFlow;
    }

    //无参构造
    public FlowBean(){
    
    

    }

    //toString
    @Override
    public String toString(){
    
    
        return "上行流量"+upFlow+"\t"+"下行流量"+downFlow+"\t"
                +"总流量: "+sumFlow;
    }

    //实现序列化和反序列化方法, 注意顺序要保持一致
    @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();
    }
}

FlowMapper

package writable;

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> {
    
    

    Text outK = new Text();
    FlowBean outV = new FlowBean();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
    
    
        //1. 分割字符串, 分隔后放入String数组
        String line = value.toString();
        String[] str = line.split("\t");
        
        //2. 取出手机号, upflow, downflow
         String phone = str[1];
        String upFlow = str[str.length-3];
        String downFlow = str[str.length-2];

        //3.  把手机号转换为long类型, 封装到Text中
        //把 upflow downflow转为long类型, 封装到flowbean中
        outK.set(phone);
        outV.setUpFlow(Long.parseLong(upFlow));
        outV.setDownFlow(Long.parseLong(downFlow));
        outV.setSumFlow();

        //4. 写出
        context.write(outK, outV);
    }
}

FlowReducer

package writable;

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> {
    
    

    FlowBean outV = new FlowBean();
    //注意, 仅仅在KEYIN(也就是Text类型的手机号)相同时候才调用reduce过程
    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
    
    
        long totalUp = 0;
        long totalDown = 0;
        //1. 对相同手机号的上行和下行分别相加
        for (FlowBean value : values) {
    
    
            totalUp += value.getUpFlow();
            totalDown += value.getDownFlow();
        }
        //2. 把totalUp, totalDown写入到FlowBean类型的outV, 并更新down, up, sum的值
        outV.setUpFlow(totalUp);
        outV.setDownFlow(totalDown);
        outV.setSumFlow();

        //3.写出去
        context.write(key, outV);
    }
}

FlowDriver


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
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, InterruptedException, ClassNotFoundException {

        //1. 获取job
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        //2. 设置jar包(driver)的路径
        job.setJarByClass(FlowDriver.class);

        //3. 关联(Mapper)和(Driver)的路径
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);

        //4. 设置Mapper输出的key 和 value类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);

        //5. 设置最终输出的key和vlue 的类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        //6.设置输入和输出的路径
        FileInputFormat.setInputPaths(job, new Path("D:\\user\\flow\\input"));
        FileOutputFormat.setOutputPath(job, new Path("D:\\user\\flow\\output"));

        //7. 提交job
        //verbose为true, 监控并打印信息
        boolean res = job.waitForCompletion(true);
        System.exit(res?0:1);
    }
}

  • 输出结果:
    在这里插入图片描述

2.4 序列化案例二, 统计分数

猜你喜欢

转载自blog.csdn.net/nmsLLCSDN/article/details/118565319