zoukankan      html  css  js  c++  java
  • hadoop深入研究:(十三)——序列化框架

    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,各个类之间通过逗号隔开,默认的SerializationWritableSerialization和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


  • 相关阅读:
    深入理解JVM内幕:从基本结构到Java 7新特性
    通过Java反射做实体查询
    Hadoop教程(一)
    很不错的js特效
    java utf8字符 导出csv 文件的乱码问题。
    spring MVC使用Interceptor做用户登录判断
    Bootstrap--全局css样式之图片
    Bootstrap-全局css样式之按钮
    Bootstrap--全局css样式之表单
    Bootstarp--全局CSS样式之表格
  • 原文地址:https://www.cnblogs.com/liango/p/7144013.html
Copyright © 2011-2022 走看看