码迷,mamicode.com
首页 > 其他好文 > 详细

十二、hadoop的序列化

时间:2019-10-29 13:26:00      阅读:74      评论:0      收藏:0      [点我收藏+]

标签:float   ica   import   apache   oat   tin   手动   内存   依赖   

一、序列化基本概述

1、何为序列化

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

2、为什么需要序列化

一般情况下,对象只存储在本地的内存中,只允许本地的进程调用。而随着分布式程序的出现,需要在不同的主机上不同进程调用对象,这就需要将对象通过网络传输到另外的主机上。但是对象不经过处理无法通过网络传输,而通过序列化处理之后,对象可以通过网络传输了。

3、java中的序列化方案

java中自行实现了序列化方案,只要定义一个类的时候实现 Serializable 接口,那么java内部就会自动实现相应的序列化。如:

public class Test implements Serializable{

    //这个序列化号是必须的,用于标识该类
    private static final long serialVersionUID = xxxx;
}

但是由于Java中的序列化接口实现的时候,会附带很多额外的信息,如各种校验信息,header,继承体系等。不便于在网络上高效传输(性能不高)。所以hadoop自己额外实现了序列化的机制,体积短小,占用带宽低,序列化和反序列化快速

二、hadoop中序列化

1、类基本依赖

hadoop中以实现 Writable 这个接口的类,就可以序列化。而且hadoop实现了许多基本类型的可序列化的类。依赖图如下所示:
技术图片

? 图 2.1 hadoop序列化依赖图

可以看到所有的可序列化的类都实现了 WritableComparable 这个接口,这个接口同时继承了 Writable 以及 Comparable 接口。下面看看这个这三个接口:

//WritableComparable.java
public interface WritableComparable<T> extends Writable, Comparable<T> {
}
/*
空的接口
*/

//Writable.java
public interface Writable {
    void write(DataOutput var1) throws IOException;

    void readFields(DataInput var1) throws IOException;
}
/*
主要包含读和写序列化对象的方法
*/

//Comparable.java
public interface Comparable<T> {
    public int compareTo(T o);
}
/*
提供序列化对象间比较的方法
*/

2、hadoop序列化类和基本类型的对照表

java类型 hadoop writable类型
boolean BooleanWritable
byte ByteWritable
Int IntWritable
float FloatWritable
long LongWritable
double DoubleWritable
string Text
map MapWritable
array ArrayWritable

3、常用序列化类的源码实现

下面挑个IntWritable这个常用的序列化类来看看源码

package org.apache.hadoop.io;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.classification.InterfaceAudience.Public;
import org.apache.hadoop.classification.InterfaceStability.Stable;

@Public
@Stable
public class IntWritable implements WritableComparable<IntWritable> {
    private int value;

    public IntWritable() {
    }

    public IntWritable(int value) {
        this.set(value);
    }

    public void set(int value) {
        this.value = value;
    }

    public int get() {
        return this.value;
    }

    //这里是实现了 writable 接口的方法
    public void readFields(DataInput in) throws IOException {
        this.value = in.readInt();
    }

    public void write(DataOutput out) throws IOException {
        out.writeInt(this.value);
    }

    //序列化对象的equals比较方法
    public boolean equals(Object o) {
        if (!(o instanceof IntWritable)) {
            return false;
        } else {
            IntWritable other = (IntWritable)o;
            return this.value == other.value;
        }
    }

    public int hashCode() {
        return this.value;
    }

    //比较对象大小的方法
    public int compareTo(IntWritable o) {
        int thisValue = this.value;
        int thatValue = o.value;
        return thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1);
    }

    public String toString() {
        return Integer.toString(this.value);
    }

    /*这里是关键,将下面的Comparator内部类作为默认的比较方法。
    因为这里采用静态代码块的方式,所以只要该类载入时,就会执行该代码块,直接创建 Comparator对象,后面无需通过外部类创建对象的方式来调用 compare方法,因为对象已经提前创建好了。比起上的 compareTo 方法,还要手动创建一个外部类对象才能调用 compareTo 方法,这里可以直接调用,效率要快。
    */
    static {
        WritableComparator.define(IntWritable.class, new IntWritable.Comparator());
    }

    //这个内部类也实现了 compare比较方法
    public static class Comparator extends WritableComparator {
        public Comparator() {
            super(IntWritable.class);
        }

        public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
            int thisValue = readInt(b1, s1);
            int thatValue = readInt(b2, s2);
            return thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1);
        }
    }
}

其他short,long的序列化类的实现也是类似的。

4、自定义序列化类

要点:
(1)必须实现 Writable接口
(2)必须有无参构造方法,因为反序列化时需要反射调用无参构造方法
(3)重写序列化方法

public void write(DataOutput out) throws IOException{
    //DataOutput接口中定义了每个基本类型序列化的方法,这里以Long为例
    out.writeLong(upFlow);
    out.writeLong(downFlow);
    out.writeLong(sumFlow);
}

(4)重写反序列化方法

public void readFields(DataInput in) throws IOException{
    upFlow = in.readLong();
    downFlow = in.readLong();
    sumFlow = in.readLong();
}

(5)序列化写入和反序列化读取时要注意,写入和读取的顺序必须完全一致
(6)按照需要可以重写 toSting 方法,便于保存在文件中的内容
(7)如果该自定义序列化类是作为键值对中的key使用的话,因为MapReduce中会以key进行排序,那么就会涉及到 key 的比较问题。所以需要实现 Comparable 接口。而该接口就得实现 compareTo 方法

public int compareTo(Test o) {
    return (-1 | 0 |1 ); 表示小于,等于,大于三种结果
}

5、属性中包含自定义类的序列化

首先属性中的自定义类也是需要实现序列化接口的。所以下面的DateDimension和ContactDimension都是已经实现序列化的了。

public class ComDimension extends BaseDimension {
    private DateDimension dateDimension = new DateDimension();
    private ContactDimension contactDimension = new ContactDimension();

//序列化就直接调用类的write方法即可,按照下面的形式
 @Override
    public void write(DataOutput dataOutput) throws IOException {
        this.dateDimension.write(dataOutput);
        this.contactDimension.write(dataOutput);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        this.dateDimension.readFields(dataInput);
        this.contactDimension.readFields(dataInput);
    }

}

十二、hadoop的序列化

标签:float   ica   import   apache   oat   tin   手动   内存   依赖   

原文地址:https://blog.51cto.com/kinglab/2446185

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!