继上一个模块之后,此次分析的内容是来到了Hadoop IO相关的模块了,IO系统的模块可谓是一个比较大的模块,在Hadoop Common中的io,主要包括2个大的子模块构成,1个是以Writable接口为主的序列化模块,还有1个是解压缩模块,所以打算分成2个模块做分析,今天来说说序列化,反序列化的分析学习,当然不只是简单的wrtite,read等的简单调度。在分析之前,看下IO包的类包含图:

在Hadoop中,你可以用java自带的序列化方式的实现,但是不推荐,因为针对Hadoop系统的分布式环境的特点,最好还是调用Hadoop自身设计的一套新的序列化实现,在java中只要接上Serializable接口代表就可以被序列化了,而在HadoZ喎�"/kf/ware/vc/" target="_blank" class="keylink">vcNbQo6y1o8jO1eK49r3Hyau1xL7NysfPwsPm1eK49r3Tv9o6PC9wPgo8cD48cHJlIGNsYXNzPQ=="brush:java;">public interface Writable { /** * Serialize the fields of this object to out. * * @param out DataOuput to serialize this object into. * @throws IOException */ void write(DataOutput out) throws IOException; /** * Deserialize the fields of this object from in. * *

For efficiency, implementations should attempt to re-use storage in the * existing object where possible.

* * @param in DataInput to deseriablize this object from. * @throws IOException */ void readFields(DataInput in) throws IOException; } 里面定义了很简单的2个方法。基本的一些数据类型都继承了这个接口,比如下面的这些:

上面的子类型只是列举出了一部分,基本上每种基本类型都有对应的Writable形式。在默认的上述子类型中都已经实现了接口的方法,所以用户在使用时可以忽略里面的细节。在上述序列化框架中还有一个特殊的设计就是WritableFactories工厂的设计。

/** Factories for non-public writables.  Defining a factory permits {@link
* ObjectWritable} to be able to construct instances of non-public classes. */
public class WritableFactories {
/**
* Writable工厂模式的变量保存
*/
private static final HashMap<class, writablefactory=""> CLASS_TO_FACTORY =
new HashMap<class, writablefactory="">(); private WritableFactories() {} // singleton
</class,></class,>

在这里维护了1个map容器。那么WritableFactory是什么呢:

/** A factory for a class of Writable.
* @see WritableFactories
*/
public interface WritableFactory {
/** Return a new instance. */
Writable newInstance();
}

其实也就是一个接口,不过,通过这个factory你可以返回你想要的Writable对象,这个工厂产生的Writable对象可能会用于某些ObjectWritable的readFileds()等等方法中。当然,Hadoop还能接入其他比较优秀的序列化框架,比如Hadoop Avro,Apache Thrift或者Google Protocol Buffer。

上面说的都是Hadoop中的各种序列化机制,那为什么Hadoop就不能采用java自带的Serialization的序列化机制呢,这个问题还得归咎于Hadoop系统的本身,Hadoop作为一种分布式平台,数据的传输都是通过RPC的形式,与网络的环境,带宽等都非常有影响。所以这时候,就要求传递的数据最好短小,精炼,序列化,反序列应该是越快速越好了哦。但是这些要求在java自带的机制中就有些不符合了。下面来看个java自带程序的序列化例子:

声明了一个类A,一个类B:

public class A implements Serializable{
protected int age;
}
public class B extends A{
/**
*
*/
private static final long serialVersionUID = -5079514899384299792L; private float height; private ContainClass contain = new ContainClass(); public void setHeight(float height){
this.height = height;
}
}

还有1个contain内部类:

public class ContainClass implements Serializable{
private int value = ;
}

然后再场景类中调用,将Object B的信息序列化到文件中:

/**
* 序列化测试类
* @author lyq
*
*/
public class Client {
public static void main(String[] args){
B b = new B();
b.age = ;
b.setHeight(); String filePath = "D:test.txt";
serialization(filePath, b);
} /**
* serialize to file
*
* @param filePath
* @param obj
* @return
* @throws RuntimeException
* if an error occurs
*/
public static void serialization(String filePath, Object obj) {
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream(filePath));
out.writeObject(obj);
out.close();
} catch (FileNotFoundException e) {
throw new RuntimeException("FileNotFoundException occurred. ", e);
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
}
}
}
}
}

最后就序列化到我的D盘的目录下了,这时如果你直接用txt打开估计是看不出什么东西的,建议,用一个代码编辑器以16进制的形式打开,就会看到下面的样子:

AC ED      0F       6C  7A
2E B9 F0 1C E5 0E F0
4C 6F
6E 6E 1C 4C 6C
7A 2F 6F 6E 6E 6C
3B 0F 6C 7A
2E D7 1A 5D
2A
1A 6C 7A
2E 6F 6E 6E 6C
F3 7A E0 C6 DF
6C 0B

关看上面的字节码也许你会头大,下面看一下对应的字符表

我们可以清晰的看到里面不关关保留了类B的本身类名信息,还有他的父类A的信息,还有A中的变量域等等,可以说还是非常庞杂的信息,设想一下,我这才是非常简单的定义了2个类,变量都只是1,2个int整形值,就已经如此庞大了,如果遇上复杂的类需要序列化,序列化后的文件大小一定不小。所以这也正是Hadoop不采用java自带的序列化机制的原因。但是既然,我们已经分析到这里了,就彻底的了解一下java的序列化机制的原理,也许很多人经常用过序列化,但还不知道序列化后的东西到底是什么吧,还是继续上年这个例子,我们通过上面的字节码去一一解读。在解读之前,有必要知道java的序列化一个简单对象的过程:

1.将对象实例相关的类元数据输出

2.递归地往上输出类的父类描述直到没有父类

3.类元数据输出完毕后,接着反着从最顶上的父类开始输出对象实例的数据值

4.再从上到下递归的输出实例的所有的值

对照上面的算法,我们试试分析一下,首先输出刚刚序列化后的字节码:

AC ED 00 05 73 72 00 0F 53 65 72 69 61 6C 69 7A 
65 54 65 73 74 2E 42 B9 81 F0 1C 86 E5 0E F0 02 
00 02 46 00 06 68 65 69 67 68 74 4C 00 07 63 6F 
6E 74 61 69 6E 74 00 1C 4C 53 65 72 69 61 6C 69 
7A 65 54 65 73 74 2F 43 6F 6E 74 61 69 6E 43 6C 
61 73 73 3B 78 72 00 0F 53 65 72 69 61 6C 69 7A 
65 54 65 73 74 2E 41 D7 91 1A 56 65 43 36 5D 02 
00 01 49 00 03 61 67 65 78 70 00 00 00 16 43 2A 
00 00 73 72 00 1A 53 65 72 69 61 6C 69 7A 65 54 
65 73 74 2E 43 6F 6E 74 61 69 6E 43 6C 61 73 73 
72 41 F3 7A E0 C6 43 DF 02 00 01 49 00 05 76 61 
6C 75 65 78 70 00 00 00 0B 

字母和大小有点不一致导致对不齐了,不过可以将就着看:

首先是AC ED:魔数,代表使用了序列化协议

00 05 :代表序列化协议版本

0x73:声明了这是一个新的对象

首先刚刚算法的第一步,输出对象类的类元数据:

首先A----->B
0x72:代表一个新的类的开始标记

00 0F:表示新的Class的名字长度,这里的值为15,因为名字为SerializeTest.B,总共15个字符

53 65 72 69 61 6C 69 7A 
65 54 65 73 74 2E 42:
表示类名,值为SerializeTest.B,通过字符的Ascall码制做映射,比如A的码制为65,a为65+32=97

B9 81 F0 1C 86 E5 0E F0:类的序列化ID,如果用户没有设定则会由算法随机生成一个8字节的ID,因为我为了区分A,B,把B设成了有序列化ID

1
2
3
4
5
public class B extends A{
    /**
     *
     */
    private static final long serialVersionUID = -5079514899384299792L;

0x02:标记号,该值表明改对象支持序列化

下面是输出里面的元数据的个数了:

00 02:代表的是所包含的变量的个数,这里是2个,B中的域1个为height,1个为contain类
接下来输出变量的信息了:

0x46:代表的是float类型,int类型为49

00 06:域名字的长度,这里为6,就是height

68 65 69 67 68 74:域名的描述,为字符height

然后是输出另一个域变量了就是内部类contain:

0x4c:域的类型,代表一个引用

00 07:域名字长度7,就是contain

63 6F 6E 74 61 69 6E:类名字描述,contain

0x74:代表用一个string来引用对象

00 1C:该string的长度为28

4C 53 65 72 69 61 6C 69 
7A 65 54 65 73 74 2F 43 6F 6E 74 61 69 6E 43 6C 
61 73 73 3B:JVM字节码标准的签名描述符,LSerializeTest/ContainClass;

0x78:contain数据类结束标记

下面开始输出父类的信息了

0x72:代表了一个新类

00 0F:名字长度15

53 65 72 69 61 6C 69 7A 
65 54 65 73 74 2E 41:描述符为SerializeTest.A

D7 91 1A 56 65 43 36 5D:又代表了1个8字节的序列化ID,这个是系统自动生成的

0x02:标记号,该值表明改对象支持序列化

00 01:代表的是所包含的变量的个数,这里是1个,1个为age

0x49:代表的是int类型

00 03:域名字的长度,这里为3,就是age

61 67 65:域名的描述,为字符age

0x78:A类的数据类结束标记,

0x70:说明A没有父类

到了这里已经把所有的类元数据输出,接着就是自顶向下输出数据的值了:

00 00 00 16:值为16*1+6=22,刚刚好是我设置的age的值。

43 2A 00 00:float类型height的值170的表现形式,不知道二者是怎么对应上的跟整形的不太一样....

下面就是输出contain域的值了,但是contain类还没有输出元数据,所以得执行类似上面的A,B操作;

0x73:声明了这是一个新的对象

0x72:代表一个新的类的开始标记

00 1A:长度域为26

53 65 72 69 61 6C 69 7A 65 54 
65 73 74 2E 43 6F 6E 74 61 69 6E 43 6C 61 73 73 :域的描述长度26个字节,为SerializeTest.ContainClass

72 41 F3 7A E0 C6 43 DF:8个字节的序列化ID

0x02:标记号,该值表明改对象支持序列化

00 01:代表域的个数为1个就是里面的int vaule

0x49:类型为int整形 00 05长度为5,

76 61 6C 75 65:描述符为value

0x78:contain类的数据类结束标记,

0x70:说明contain没有父类

描述完整个内部类的元数据后,回到刚刚那个话题,就是输出值了,就剩下最后的contain.value:

00 00 00 0B :就是十进制的11

好了我们大致理一下序列化的过程

输出B的变量信息----->输出父类A的变量信息------>输出A的变量的值------》输出B的变量的值------》输出contain类的变量值时发现没有输出内部类contain的变量信息-----》再次输出contain的变量信息------》最后输出contain类的变量值

总的来说,还是比较复杂的吧,跟jvm的程序的字节码的生成比较类似。

转自:https://www.2cto.com/kf/201412/357242.html

为什么hadoop中用到的序列化不是java的serilaziable接口去序列化而是使用Writable序列化框架的更多相关文章

  1. SQL on Hadoop中用到的主要技术——MPP vs Runtime Framework

    转载声明 本文转载自盘点SQL on Hadoop中用到的主要技术,个人觉得该文章对于诸如Impala这样的MPP架构的SQL引擎和Runtime Framework架构的Hive/Spark SQL ...

  2. 盘点SQL on Hadoop中用到的主要技术

    转载自:http://sunyi514.github.io/2014/11/15/%E7%9B%98%E7%82%B9sql-on-hadoop%E4%B8%AD%E7%94%A8%E5%88%B0% ...

  3. MapReduce框架原理-Writable序列化

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

  4. Java下利用Jackson进行JSON解析和序列化

    Java下利用Jackson进行JSON解析和序列化   Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行 ...

  5. 一起学 Java(四) File、Try 、序列化、MySQL、Socket

    一.Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基 ...

  6. Effective Java 第三版—— 87. 考虑使用自定义序列化形式

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  7. 初识序列化和反序列化,使用BinaryFormatter类、ISerializable接口、XmlSerializer类进行序列化和反序列化

    序列化是将对象转换成字节流的过程,反序列化是把字节流转换成对象的过程.对象一旦被序列化,就可以把对象状态保存到硬盘的某个位置,甚至还可以通过网络发送给另外一台机器上运行的进程.本篇主要包括: ● 使用 ...

  8. hadoop学习笔记(五):java api 操作hdfs

    HDFS的Java访问接口 1)org.apache.hadoop.fs.FileSystem 是一个通用的文件系统API,提供了不同文件系统的统一访问方式. 2)org.apache.hadoop. ...

  9. 【Java面试题】45 什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用。

    我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象,例如,要将java对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个java对象 ...

随机推荐

  1. js面向对象怎么理解

    js面向对象怎么理解 <一>. 认识对象.首先要认识对象:在编程中,对象(object)是具体的某一个实例,唯一的某一个个体.如:电脑就是一个统称,而你面前的这一台电脑就是对象.而电脑的统 ...

  2. PP: Soft-DTW: a differentiable loss function for time-series

    Problem: new loss Label: new loss; Abstract: A differentiable learning loss; Introduction: supervise ...

  3. 检测到 LoaderLock Message: 正试图在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。

    解决方法: 调试状态=>异常(Ctrl+Alt+E)=>Managed Debuggin Assistants=>LoaderLock 的选中状态去掉即可.

  4. 无法解析的外部符号 _snprintf

    VS2010下: 在使用第三方静态库 遇到无法解析的外部符号 _snprintf . 编译第三方库的时候 看到有 warning C4013: 'snprintf' undefined; assumi ...

  5. 【并发那些事】线程有序化神器CompletionService

    前言 话说有一天,产品经理突然找到正在摸鱼的你. 产品:『我们要加一个聚合搜索功能,当用户在我们网站查询一件商品时,我们分别从 A.B.C 三个网站上查询这个信息,然后再把得到的结果返回给用户』 你: ...

  6. mac /windows

    1.mac 和 windows 同样的浏览器展示的样式有偏差 考虑:字体的问题,mac/windows 的字体不一样 font-family 用法

  7. 查看Spark与Hadoop等其他组件的兼容版本

    安装与Spark相关的其他组件的时候,例如JDK,Hadoop,Yarn,Hive,Kafka等,要考虑到这些组件和Spark的版本兼容关系.这个对应关系可以在Spark源代码的pom.xml文件中查 ...

  8. 2018ICPC南京站Problem A. Adrien and Austin

    题意: n个石头再1-n的位置上,两个人轮流取时候,必须取连续的一段,最多取k个,不能取为输,问谁会赢 解析: 当k大于等于2时,先手总能把石头分成相等的两部分,此时后手无论怎么走,先手在对称的位置选 ...

  9. 【Python】画一个心形

    #!/usr/bin/env python # -*- coding:utf-8 -*- import turtle import time # 画心形圆弧 def hart_arc(): for i ...

  10. WebService 之 SoapHeader

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...