Hadoop的I/O操作——序列化(一)
1. 序列化
(1)基本定义
序列化指的是将结构化的对象转化为字节流,以便在网络上进行传输,或是写入磁盘里进行永久存储;反序列化则是将字节流转回结构化对象。
(2)应用领域
序列化主要用于分布式数据处理的两大领域:进程间通信和永久存储(即数据的持久化)。
(3)RPC序列化机制特点
Hadoop利用了RPC来实现进程间的通讯,RPC的序列化机制有以下特点:
1)紧凑
紧凑格式能充分利用网络带宽(数据中心最稀缺的资源),加快传输速度。
2)快速
能减少序列化和反序列化的开销。
3)可扩展
可以随时增加方法调用的新参数。
4)支持互操作
客户端和服务器端可以用不同的语言来实现。
2. Writable接口
2.1 接口介绍
org.apache.hadoop.io.Writable接口是Hadoop序列化机制的核心,该接口定义了两个方法:
package org.apache.hadoop.io;
import java.io.DataOutput; //二进制输出流
import java.io.DataInput; //二进制输入流
import java.io.IOException;
public interface Writable {
//将对象序列化到二进制输出流
void write(DataOutput out) throws IOException;
//从二进制输入流将对象反序列化
void readFields(DataInput in) throws IOException;
}
Hadoop中有很多类对Writable接口进行了实现,例如IntWritable类,它是对Java数据类型int的包装类。实例化一个IntWritable对象的方法如下:
//法一:
IntWritable writable = new IntWritable();
writable.set(1);
//法二:
IntWritable writable = new IntWritable(1);
2.2 实例1
实现目标:
实现一个方法serialize,将一个IntWritable的序列化格式展现出来。
实现过程:
(1)编写MyWritable.java:
import java.io.*;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.*;
public class MyWritable {
public static byte[] serialize(Writable writable) throws IOException {
/*
在这个方法中,数据的流向是:writable.write 到 DataOutputStream 到 ByteArrayOutputStream
*/
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dataout = new DataOutputStream(out);
writable.write(dataout);
dataout.close();
return out.toByteArray();
}
public static void main(String[] args) throws Exception{
IntWritable w1 = new IntWritable(5);
byte[] b1 = serialize(w1);
System.out.println(StringUtils.byteToHexString(b1));
}
}
(2)编译后利用hadoop命令运行该类:
root@6b08ff31fc7f:/hadoop/hadoop-2.9.1/test/hadoop-io# hadoop MyWritable
00000005
2.3 实例2
实现目标:
实现一个方法deserialize,将一个IntWritable的反序列结果展现出来。
实现过程:
(1)编写MyWritable.java:
import java.io.*;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.*;
public class MyWritable {
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();
}
public static void deserialize(byte[] bytes, Writable writable) throws IOException {
/*
此方法中的数据流向是:byte[] 到 ByteArrayInputStream 到 DataInputStream 到 writable.readFields 到 writable
*/
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
DataInputStream datain = new DataInputStream(in);
writable.readFields(datain); /*writable将datain输入的字节流反序列化为writable对象*/
datain.close();
in.close();
}
public static void main(String[] args) throws Exception{
IntWritable w1 = new IntWritable(5);
byte[] b1 = serialize(w1);
System.out.println(StringUtils.byteToHexString(b1));
IntWritable w2 = new IntWritable(); /*此时w2里没有值*/
deserialize(b1, w2);
System.out.println(w2.get()); /*获取反序列后的对象值*/
}
}
(2)编译后利用hadoop命令运行该类:
root@6b08ff31fc7f:/hadoop/hadoop-2.9.1/test/hadoop-io# hadoop MyWritable
00000005
5
3. WritableComparable和WritableComparator
对于MapReduce来说,类型的比较很重要,因为中间有个基于键的排序阶段。Java里提供了Comparable和Comparator两种接口,Hadoop中也提供了对应的接口和类,并且是继承自Java提供的接口的,下面就分别来进行介绍。
3.1 Comparable
(1)java.lang.Comparable接口
在Java里,任何需要进行比较的类型都必须实现java.lang.Comparable接口:
package java.lang;
public interface Comparable<T> {
public int compareTo(T o);
}
举个实际的例子,class Hellow实现了Comparable接口,并且重写compareTo函数:
package come.test.one;
public class Hellow implements Comparable{
int i;
public Hellow(int j) {
i = j;
}
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
Hellow temp = (Hellow)o;
if(i == temp.i) {
return 0;
}
else if(i > temp.i) {
return 1;
}
else {
return -1;
}
}
}
用class hehe中的main函数测试:
package come.test.one;
public class hehe {
public static void main(String[] args) {
Hellow h1 = new Hellow(1);
Hellow h2 = new Hellow(-6);
int k = h1.compareTo(h2);
System.out.print(k);
//输出的k为1
}
}
(2)org.apache.hadoop.io. WritableComparable接口
在Hadoop中,org.apache.hadoop.io. WritableComparable接口是一个很重要的接口,它继承自org.apache.hadoop.io. Writable接口和java.lang.Comparable接口:
package org.apache.hadoop.io;
public Interface WritableComparable<T> extends Writable,Comparable<T> {
}
3.2 Comparator
(1)java.lang.Comparator接口
在Java中定义了java.util.Comparator接口,这是一个比较器接口,用于比较两个对象:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
}
举个实际的例子,class Hellow实现了Comparator接口,并且重写compare函数:
package come.test.one;
import java.util.Comparator;
public class Hellow implements Comparator{
int i;
public Hellow(int j) {
i = j;
}
@Override
public int compare(Object arg0, Object arg1) {
// TODO Auto-generated method stub
Hellow h1 = (Hellow)arg0;
Hellow h2 = (Hellow)arg1;
if(h1.i == h2.i) {
return 0;
}
else if(h1.i > h2.i) {
return 1;
}
else {
return -1;
}
}
}
类hehe对其进行测试:
package come.test.one;
public class hehe {
public static void main(String[] args) {
Hellow h1 = new Hellow(1);
Hellow h2 = new Hellow(-6);
Hellow h3 = new Hellow(2);
int i = h3.compare(h1, h2);
System.out.println(i);
//输出为1
}
}
(2)org.apache.hadoop.io. RawComparator接口
Hadoop提供了一个org.apache.hadoop.io. RawComparator接口,它继承自java.lang.Comparator接口,且该接口还在Comparator接口的基础上新添了一个接口方法:
package org.apache.hadoop.io;
import java.util.Comparator;
public Interface RawComparator<T> extends Comparator<T> {
//新添加的接口方法
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)
}
RawComparator接口定义的这个compare方法可以直接在字节流这一级别进行比较,而不需要将字节流反序列化为对象再进行比较,因此它的效率要高很多。
(3)org.apache.hadoop.io. WritableComparator类
WritableComparator类实现了RawComparator接口,可以用来比较WritableComparable对象,下面举个例子。
A. 编写MyWritable_1.java:
import java.io.*;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.*;
public class MyWritable_1 {
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();
}
public static void main(String[] args) throws Exception{
WritableComparator comparator = WritableComparator.get(IntWritable.class);
IntWritable w1 = new IntWritable(5);
IntWritable w2 = new IntWritable(10);
/*此处调用的是Java API的comparator接口方法,在对象级进行比较*/
int i = comparator.compare(w1, w2);
System.out.println("i is " + i);
//ouput is -1
byte[] bytes_1 = serialize(w1);
byte[] bytes_2 = serialize(w2);
/*此处调用的是HadoopD RawComparator的comparator接口方法,在字节流级别进行比较*/
int j = comparator.compare(bytes_1, 0, bytes_1.length, bytes_2, 0, bytes_2.length);
System.out.println("j is " + j);
}
}
B. 用hadoop命令运行该类:
root@6b08ff31fc7f:/hadoop/hadoop-2.9.1/test/hadoop-io# hadoop MyWritable_1
i is -1
j is -1
可以看到,两种比较的结果是一样的。
3.3 总结
这些接口和类,可以用UML图来展示它们之间的关系: