hadoop深入研究:(十三)——序列化框架

Mapreduce之序列化框架(转自http://blog.csdn.net/lastsweetop/article/details/9376495)

框架简介

大部分的MapReduce程序都使用Writable键–值对作为输入和输出,但这并不是Hadoop强制使用的,其他序列化机制也能和Hadoop配合,并应用于MapReduce中。

目前,除了前面介绍过的Java序列化机制和Hadoop使用的Writable机制,还流行其他序列化框架,如Hadoop Avro、Apache Thrift和Google Protocol Buffer。

MapReduce仅仅可以支持Writable做key,value吗?答案是否定的。事实上,可以使用任何类型:只要能有一种机制能对每个类型进行类型与二进制表示的来回转换。为此Hadoop提供了一个针对序列化做替换的框架来支持,他们在org.apache.hadoop.io.serializer包中,Writable可以作为MapReduce支持的类型也是因为实现了这个框架,类不多,我们从几个接口说起。

Hadoop提供了一个简单的序列化框架API,用于集成各种序列化实现,该框架由Serialization实现(在org.apache.hadoop.io.serializer包中)。

Serialization是一个接口,使用抽象工厂的设计模式.定义了一组接口,判断是否支持输入的类,根据输入的类给出序列化接口和反序列化接口

/**
 * 
 * 包装一个序列化/反序列化对 (抽象工厂类)*/public interface Serialization<T> {  
  /**
   * 允许客户端进行测试:给的序列化是否支持给定的类   */
  boolean accept(Class<?> c);  
  /**
   * 获得用于序列化对象的Serializer实现   */
  Serializer<T> getSerializer(Class<T> c);  
  /**
   * 获得用于反序列化对象的Deserializer实现*/
  Deserializer<T> getDeserializer(Class<T> c);
}

参考了:Hadoop的简单序列化框架 - cjt1991 - 博客园 - http://www.cnblogs.com/java-cjt/p/4443852.html

Serializer 定义了一组接口,打开流,序列化,关闭流

如果需要使用Serializer来执行序列化,一般需要通过Open方法来打开Serializer,open()方法传入一个底层的流对象,然后就可以使用serializer()方法序列化对象对底层的流中。最后序列化结束时,通过close()方法关闭Serializer。

public interface Serializer <T>  {  
    void open(java.io.OutputStream outputStream) throws java.io.IOException;  
  
    void serialize(T t) throws java.io.IOException;  
  
    void close() throws java.io.IOException;  
}  

Deserializer

定义了一组接口,打开流,反序列化,关闭流

public interface Deserializer <T>  {  
    void open(java.io.InputStream inputStream) throws java.io.IOException;  
    T deserialize(T t) throws java.io.IOException;  
    void close() throws java.io.IOException;  
}

copy

WritableSerialization

如果你想自己定义一个类似Writable这样的框架,那么你首先需要的就是实现上面三个接口,那么我们先来看下Writable是如何实现的。

public class WritableSerialization extends Configured   
  implements Serialization<Writable> {  
    
  static class WritableDeserializer extends Configured   
    implements Deserializer<Writable> {  
  
    private Class<?> writableClass;  
    private DataInputStream dataIn;  
      
    public WritableDeserializer(Configuration conf, Class<?> c) {  
      setConf(conf);  
      this.writableClass = c;  
    }  
      
    public void open(InputStream in) {  
      if (in instanceof DataInputStream) {  
        dataIn = (DataInputStream) in;  
      } else {  
        dataIn = new DataInputStream(in);  
      }  
    }  
      
    public Writable deserialize(Writable w) throws IOException {  
      Writable writable;  
      if (w == null) {  
        writable   
          = (Writable) ReflectionUtils.newInstance(writableClass, getConf());  
      } else {  
        writable = w;  
      }  
      writable.readFields(dataIn);  
      return writable;  
    }  
  
    public void close() throws IOException {  
      dataIn.close();  
    }  
      
  }  
    
  static class WritableSerializer implements Serializer<Writable> {  
  
    private DataOutputStream dataOut;  
      
    public void open(OutputStream out) {  
      if (out instanceof DataOutputStream) {  
        dataOut = (DataOutputStream) out;  
      } else {  
        dataOut = new DataOutputStream(out);  
      }  
    }  
  
    public void serialize(Writable w) throws IOException {  
      w.write(dataOut);  
    }  
  
    public void close() throws IOException {  
      dataOut.close();  
    }  
  
  }  
  
  public boolean accept(Class<?> c) {  
    return Writable.class.isAssignableFrom(c);  
  }  
  
  public Deserializer<Writable> getDeserializer(Class<Writable> c) {  
    return new WritableDeserializer(getConf(), c);  
  }  
  
  public Serializer<Writable> getSerializer(Class<Writable> c) {  
    return new WritableSerializer();  
  }  
  
}

两个内部静态类分别实现Serializer和Deserializer接口,然后getSerializer和getDeserializer分别实例化WritableSerializer和WritableDeserializer,

accept方法仅仅是判断输入类是否是Writable的子类。

通过"io.serializations"指定以实现Serialization,各个类之间通过逗号隔开,默认的Serialization有WritableSerialization和Avro中Serialization,

这也就是说默认情况下,只有Writable和Avro里的对象可以在MapReduce中使用。

那么你可能有疑问了,hadoop是如何知道一个类该交给哪个Serialization呢,答案也在这个包中,请看

SerializationFactory

先看他的构造器

[java] view plain copy

public SerializationFactory(Configuration conf) {  
    super(conf);  
    for (String serializerName : conf.getStrings("io.serializations",   new String[]{"org.apache.hadoop.io.serializer.WritableSerialization"})) {  
      add(conf, serializerName);  
    }  
  }  

可知他是从"io.serializations"属性指定的实现了Serialization的类,然后再看他是如何知道选哪个Serialization的

[java] view plain copy

public <T> Serialization<T> getSerialization(Class<T> c) {  
   for (Serialization serialization : serializations) {  
     if (serialization.accept(c)) {  
       return (Serialization<T>) serialization;  
     }  
   }  
   return null;  
 }  

好吧,就是这么简单,判断一下是否是对应的子类而已。

这个包里还实现了JavaSerialization,其实就是Java对象的序列化,很多人觉得,这个好简单的,我只要实现java中的序列化接口就可以了,

不用那么费事搞什么Writable和Avro,但是,千万别这么想,非常不推荐使用java对象的序列化,并且详尽的解释为什么不推荐:

Hadoop目前支持两个Serialization实现分别是支持Writable机制的WritableSerialization和支持Java序列化的JavaSerialization。通过JavaSerialization可以在MapReduce程序中方便的使用java类型,如int或String,但Java的ObjectSerialization不如Hadoop的序列化机制有效,非特殊情况不要尝试

为什么不使用java序列化

1.Java序列化不够灵活,为了更好的控制序列化的整个流程所以使用Writable

2.java序列化不符合序列化的标准,没有做一定的压缩,java序列化首先写类名,然后再是整个类的数据,而且成员对象在序列化中只存引用,成员对象的可以出现的位置很随机,既可以在序列化的对象前,也可以在其后面,这样就对随机访问造成影响,一旦出错,整个后面的序列化就会全部错误,但是
Writable完美的弥补了这一点,因为Writable中每一条纪录间是相互独立的
3.Java序列化每次序列化都要重新创建对象,内存消耗大,而Writable是可以重用的。

Hadoop序列化机制的特征

        紧凑:由于带宽是Hadoop集群中最稀缺的资源,一个紧凑的序列化机制可以充分利用数据中心的带宽。

        快速:在进程间通信(包括MapReduce过程中涉及的数据交互)时会大量使用序列化机制;因此,必须尽量减少序列化和反序列化的开销。

        可扩展:随着系统的发展,系统间通信的协议会升级,类的定义会发生变化 ,序列化机制需要支持这些升级和变化。

        可操作:可以支持不同开发语言的通信:如C++和Java间的通信。这样的通信,可以通过文件 (需要精心设计文件的格式)或者后者介绍的IPC机制实现。

 

Java序列化在Hadoop中不适应原因:占用存储空间大,在反序列化中不断创建大量新对象,而Hadoop反序列化可以重用对象,在已有对象上进行反序列化操作。来源: http://blog.sina.com.cn/s/blog_7ed002b30101j6pa.html

序列化IDL

为了和其他语言交互,必须定义序列化的IDL,原先定义的IDL在org.apache.hadoop.record包里,但是后来一直没用起来就淘汰掉了,现在比较常用的就是Avro,后面我们会重点着墨讲解。

Apache的Thrift和Google的Protocol Buffer也是比较流行的序列化框架,但是在Hadoop里使用是有限的,只用于RPC和数据交互,不过有一个开源项目elephant-bird可以把他们使用在MapReduce上。

总结:

A 默认序列化框架是 Writable接口, 缺点: 缺乏语言的可移植性

B 不使用java Serialization, 缺点: 不够精简, 用起来非常纠结, 无法做到 精简,快速, 可扩展, 支持互操作

C Apache Thrift 一般用来作为二进制 数据的永久存储格式, Mapreduce格式对该类的支持有限

D Google Protocol框架 一般用来做二进制数据的永久存储格式,Mapreduce格式对该类的支持有限

E: Avro 更加有生命力, 与编程语言无关, 非常使用hadoop的大规模数据处理。

Avro模式议案使用JSON来写, 数据通常采用二进制格式来编码

和其他序列化类库想比, Avro的性能更好。

=======================================================================================

hadoop权威指南 第三版 page 127

原文:hadoop序列化框架 - qiezikuaichuan的专栏 - CSDN博客 - http://blog.csdn.net/qiezikuaichuan/article/details/48676181

参考:

hadoop深入研究:(十三)——序列化框架 - 独自登高楼 望断天涯路 - CSDN博客 - http://blog.csdn.net/lastsweetop/article/details/9376495#t5

通俗 Python 设计模式——工厂模式 - 知乎专栏 - https://zhuanlan.zhihu.com/p/23803353

抽象工厂模式和工厂模式的区别? - 知乎 - https://www.zhihu.com/question/20367734

抽象工厂模式 - cbf4life - 博客园 - http://www.cnblogs.com/cbf4life/archive/2009/12/23/1630612.html

《JAVA与模式》之抽象工厂模式 - java_my_life - 博客园 - http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html

设计模式系列 - 抽象工厂模式 - 后端 - 掘金 - https://juejin.im/entry/58ddc44a8d6d8100613ee7d0

Java 的 23 种设计模式全解析 - 后端 - 掘金 - https://juejin.im/entry/58faca0a1b69e600588cd952
java-设计模式之-抽象工厂模式 | 戒修-沉迷技术的小沙弥 - https://leokongwq.github.io/2016/11/26/java-DesignPatterns-abstractFactory.html

hadoop深入研究:(十三)——序列化框架的更多相关文章

  1. 5.3.4 Hadoop序列化框架

    序列化框架 除了writable实现序列化之外,只要实现让类型和二进制流相互转换,都可以作为hadoop的序列化类型,为此Hadoop提供了一个序列化框架接口,他们在org.apache.hadoop ...

  2. Mapreduce之序列化框架(转自http://blog.csdn.net/lastsweetop/article/details/9376495)

    框架简介 MapReduce仅仅可以支持Writable做key,value吗?答案是否定的.事实上,一切类型都是支持的,只需满足一个小小的条件:每个类型是以二进制流的形式传输.为此Hadoop提供了 ...

  3. Hadoop的简单序列化框架

    Hadoop提供了一个加单的序列化框架API,用于集成各种序列化实现.该框架由Serialization实现. 其中Serialization是一个接口,使用抽象工厂的设计模式,提供了一系列和序列化相 ...

  4. 为什么hadoop中用到的序列化不是java的serilaziable接口去序列化而是使用Writable序列化框架

    继上一个模块之后,此次分析的内容是来到了Hadoop IO相关的模块了,IO系统的模块可谓是一个比较大的模块,在Hadoop Common中的io,主要包括2个大的子模块构成,1个是以Writable ...

  5. [java]序列化框架性能对比(kryo、hessian、java、protostuff)

    序列化框架性能对比(kryo.hessian.java.protostuff) 简介:   优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Pro ...

  6. 二进制序列化框架easypack发布啦!

    简介 easypack是基于boost.serialization的二进制序列化框架,使用极其方便. Examples 基本类型 int age = 20; std::string name = &q ...

  7. hadoop深入研究:(五)——Archives

    转载请注明来源地址:http://blog.csdn.net/lastsweetop/article/details/9123155 简介 我们在hadoop深入研究:(一)——hdfs介绍里已讲过, ...

  8. hadoop深入研究:(七)——压缩

    转载请标明出处:hadoop深入研究:(七)——压缩 文件压缩主要有两个好处,一是减少了存储文件所占空间,另一个就是为数据传输提速.在hadoop大数据的背景下,这两点尤为重要,那么我现在就先来了解下 ...

  9. Java序列化框架性能比較

    博客: http://colobu.com jvm-serializers提供了一个非常好的比較各种Java序列化的的測试套件. 它罗列了各种序列化框架. 能够自己主动生成測试报告. 我在AWS c3 ...

随机推荐

  1. bzoj1999 / P1099 树网的核

    P1099 树网的核 (bzoj数据加强) 前置知识:树的直径 (并不想贴我的智障写法虽然快1倍但内存占用极大甚至在bzoj上MLE) 正常写法之一:用常规方法找到树的直径,在直径上用尺取法找一遍,再 ...

  2. github上fork别人的代码之后,如何保持和原作者同步的更新

    1.从自己fork之后的版本库clone $  git clone -o chucklu https://github.com/chucklu/Hearthstone-Deck-Tracker.git ...

  3. 近期(17年三月至四月) TODOlist

    前言 今天突然发现好久没有更新博客了,最近确实记录的内容不多,也沉迷Gayhub无法自拔,和那只北极熊愉快的玩耍.不知道为什么记性变差了许多,所以简单的列一个近期的TODOlist给自己一个提醒. T ...

  4. mybatis报Invalid bound statement (not found) 分析

      解决问题的步骤,请参考: 1.mapper.xml要和对应的mapper接口在同一个包下,包名要一模一样. 2.Mapper接口中的方法在Mapper.xml中没有,然后执行Mapper接口的方法 ...

  5. 请问使用jmeter在tcp取样器测试中服务器名称或ip,端口可以填变量值吗?

    请问使用jmeter在tcp取样器测试中服务器名称或ip,端口可以填变量值吗?

  6. Nginx 正则匹配

    目录 Nginx 正则表达式之匹配操作符 过期缓存 针对浏览器 针对文件类型 针对文件夹 判断文件,文件夹 设置某些类型文件的浏览器缓存时间 匹配到所有uri 全局变量 常用正则 Nginx 正则表达 ...

  7. Html之网页分屏浏览

    Hi!  Every Body!Welcome to my blog! My name is Caiduping,I hope we learn to make progress together! ...

  8. nodejs项目的model操作mongo

    想想以前学习hibernate的时候,学习各种表和表之间的映射关系等一对多,多对一,多对多,后来到了工作中,勇哥告诉我, 那时在学习的时候,公司中都直接用外键关联. 这里我们学习下,如何在Nodejs ...

  9. 雷林鹏分享:Ruby 多线程

    Ruby 多线程 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程. 线程是程序中一个单一的顺序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程. Ruby 中我们可以通 ...

  10. 玲珑杯 round18 A 计算几何瞎暴力

    题目链接 : http://www.ifrog.cc/acm/problem/1143 当时没看到坐标的数据范围= =看到讨论才意识到,不同的坐标最多只有1k多个,完全可以暴力做法,不过也要一些技巧. ...