序列化概念
序列化(Serialization)是指把结构化对象转化为字节流以便在网络上传输或写到磁盘进行永久存储的过程。
反序列化(Deserialization)是序列化的逆过程,即把字节流转回结构化对象。
hadoop 不使用jdk自带序列化机制
在java程序中若要序列化某个类,让该类implements java.io.Serializable,我们不用自己去序列化,JVM会帮我们做好序列化的工作。(例如:定义一个叫Animal的基类,该类有一些公共属性及方法,再定义一个Monkey类,该类继承了Animal类,则该类有了Animal类的公共属性及方法,可能还有自己特殊的特性。)使用jdk的序列化机制序列化Monkey类时,还要保存它与父类Animal的继承结构。反序列化时当然这些也是需要保留的。
然而hadoop要序列化某个类是根本不需要保留它们的继承结构,只需要能够实现持久化,能够实现快速传递。这样的话,Java的序列化就会非常冗余,效率低。
hadoop序列化的特点
- 紧 凑:高效使用存储空间。
- 快 速:读写数据的额外开销小
- 可扩展:可透明地读取老格式的数据
- 互操作:支持多语言的交互
Hadoop序列化的作用
- 序列化在分布式环境的两大作用:进程间通信,永久存储。
- hadoop节点间通信(通过RPC远程过程调用),RPC协议将消息序列化成二进制流后发送到远程节点,远程节点接着将二进制流反序列化为原始消息。
Hadoop的序列化格式:Writable
- MR的任意Key和Value必须实现Writable接口
@InterfaceAudience.Public
@InterfaceStability.Stable
public interface Writable {
/** Serialize the fields of this object to out. */
void write(DataOutput out) throws IOException;
/** Deserialize the fields of this object from in. */
void readFields(DataInput in) throws IOException;
}
- MR的任意key必须实现WritableComparable接口
@InterfaceAudience.Public
@InterfaceStability.Stable
public interface WritableComparable<T> extends Writable, Comparable<T> {
}
- 使用Writable接口与序列化和反序列化示例代码
package hadoop.mr.writable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestWritable {
byte[] bytes = null;
/** 初始化一个IntWritable实例,并且调用系列化方法 */
@Before
public void init() throws IOException {
IntWritable writable = new IntWritable(163);
bytes = serialize(writable);
}
/** 将一个实现了Writable接口的对象序列化成字节流 */
public static byte[] serialize(Writable writable) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(out);
writable.write(dataOut);
dataOut.close();
return out.toByteArray();
}
/** 一个IntWritable序列号后的四个字节的字节流 并且使用big-endian的队列排列 */
@Test
public void testSerialize() throws IOException {
Assert.assertEquals(bytes.length, 4);
Assert.assertEquals(StringUtils.byteToHexString(bytes), "000000a3");
}
/** 将字节流反序列为Writable接口的对象 */
public static byte[] deserialize(Writable writable, byte[] bytes)
throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
DataInputStream dataIn = new DataInputStream(in);
writable.readFields(dataIn);
dataIn.close();
return bytes;
}
/** 创建一个没有值的IntWritable对象,并且通过调用反序列化方法将bytes的数据读入到它里面 通过调用它的get方法,获得原始的值,163 */
@Test
public void testDeserialize() throws IOException {
IntWritable newWritable = new IntWritable();
deserialize(newWritable, bytes);
Assert.assertEquals(newWritable.get(), 163);
}
}
WritableComparable和comparators
- MapReduce在排序部分要根据key值的大小进行排序,因此类型的比较相当重要,RawComparator是Comparator的增强版。RawComparator允许其实现直接比较数据流中的记录,无须先把数据反序列化为对象,这样便避免了新建对象的额外开销。
package org.apache.hadoop.io;
public interface RawComparator <T> extends java.util.Comparator<T> {
int compare(byte[] bytes, int i, int i1, byte[] bytes1, int i2, int i3);
}
- 使用RawComparator示例代码
package hadoop.mr.writable;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.RawComparator;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestComparator {
RawComparator<IntWritable> comparator;
IntWritable w1;
IntWritable w2;
/** 获得IntWritable的comparator,并初始化两个IntWritable */
@Before
public void init() {
this.comparator = WritableComparator.get(IntWritable.class);
this.w1 = new IntWritable(163);
this.w2 = new IntWritable(76);
}
/** RawComparator:直接比较两个对象大小 */
@Test
public void testComparator() {
Assert.assertTrue(this.comparator.compare(w1, w2) > 0);
}
/** 序列化后进行直接比较 */
@Test
public void testcompare() throws IOException {
byte[] b1 = serialize(w1);
byte[] b2 = serialize(w2);
Assert.assertTrue(this.comparator.compare(b1, 0, b1.length, b2, 0,
b2.length) > 0);
}
/**
* 将一个实现了Writable接口的对象序列化成字节流
*
* @param writable
* @return
* @throws java.io.IOException
*/
public static byte[] serialize(Writable writable) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(out);
writable.write(dataOut);
dataOut.close();
return out.toByteArray();
}
}