thrift, Protocol Buffers and MsgPack 的序列化对比

啥是序列化?

序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

为啥要序列化?

  1、以某种存储形式使自定义对象持久化

  2、将对象从一个地方传递到另一个地方。
  3、使程序更具维护性
以上摘自百度百科。


一般的,在做网络服务架构的时候,需要同步数据,为了保证数据的可用性,在传输数据之前需要对数据进行序列化的操作。对于多种服务之间的数据传输,涉及到了几个会影响架构选择的问题:

  • 效率
  • 接口的耦合性(即一台服务器升级了接口,其它服务器是否需要执行同步升级,旧的服务器是否能够兼容升级的数据接口)
  • 语言的扩展性
  • 数据结构的支持

效率
一般对于网络服务来说,效率是一个非常重要的衡量因素。因此,需要细致的考量该问题。关于效率对比,在网上有很多的数据,在这里,笔者还是做了简单的测试。


接口的耦合性
关于接口的耦合性,在这里只对thrift和Protocol Buffers进行说明,因为msgpack确定是耦合的。如果服务端和客户端的接口定义不一致,必定造成读写错误,这里对msgpack就不再赘述。
  • thrift    ☆☆
    为了测试增加了接口是否仍然可用,笔者写了如下测试代码:
    struct Person{                                                                                                                               
            1:i64 id, 
            2:string name
    }
    
    struct Student{
            1:i64 id, 
            2:string name,
            3:string school
    }
    
    struct Student2{
            1:i64 id, 
            2:string school,
            3:string name
    }
    
    struct Student3{
            1:i64 id, 
            2:double fee = 10000.0,
            3:string school,
            4:string name
    }
    以上的是thrift的定义文件,假定Person是老接口,Student系列是升级的几个接口。
    进行序列化和反序列化的代码如下:
        using apache::thrift::transport::TMemoryBuffer;
        using apache::thrift::protocol::TBinaryProtocol;
        using boost::shared_ptr;
    
        shared_ptr strBuffer(new TMemoryBuffer());
        shared_ptr binaryProtcol(new TBinaryProtocol(strBuffer));
    
        Student3 a;
        a.id = 10; 
        a.name ="holla back a";
        a.school = "pku";                                                                                                                        
    
        a.write(binaryProtcol.get());
        std::string serialized = strBuffer->getBufferAsString();
    
        shared_ptr strBuffer2(new TMemoryBuffer());
        shared_ptr binaryProtcol2(new TBinaryProtocol(strBuffer2));
    
        strBuffer2->resetBuffer((uint8_t*)serialized.data(), serialized.length());
        Person a2; 
        a2.read(binaryProtcol2.get());
    

    如上是核心的测试代码,测试方法为发送不同的Student数据,查看收到的person数据是否正常。
    测试结果如下:
    Student和person可以完全实现传输;Student2传给person之后,person的name字段对应到了Student2的school字段;Student3传输给person之后,name字段为空(对应的位置上为double)。
    根据以上的测试结果得出结论:
    当且仅当不改变原有接口数据顺序以及类型的前提下,通过在结构的后端增加数据,不影响两端的通信。
    至于原理,可以查看产生代码的read以及write方法的源代码,此文档中也有一些探讨 http://www.cnblogs.com/egmkang/archive/2011/05/14/2046335.html
    P.S. thrift支持嵌套的数据类型结构,例如,一个新的数据类型可以定义如下:
    struct Company{
        1:i64 id,
        2:string name,
        3:listids,
        4:setiSet,
        5:map iMap,
        6:Student s//OK
    } 

  • Protocol Buffers    ☆☆☆☆
    由于protocol buf 中存在required,optional,以及repeated字段,所以,protocol buf是接口耦合性最低的一种序列化方式。


语言扩展性
  • thrift 
    Available generators (and options):
      as3 (AS3):
        bindable:          Add [bindable] metadata to all the struct classes.
      c_glib (C, using GLib):
      cocoa (Cocoa):
        log_unexpected:  Log every time an unexpected field ID or type is encountered.
      cpp (C++):
        cob_style:       Generate "Continuation OBject"-style classes.
        no_client_completion:
                         Omit calls to completion__() in CobClient class.
        templates:       Generate templatized reader/writer methods.
        pure_enums:      Generate pure enums instead of wrapper classes.
        dense:           Generate type specifications for the dense protocol.
        include_prefix:  Use full include paths in generated files.
      csharp (C#):
      delphi (delphi):
        ansistr_binary:  Use AnsiString as binary properties.
      erl (Erlang):
      go (Go):
      hs (Haskell):
      html (HTML):
      java (Java):
        beans:           Members will be private, and setter methods will return void.
        private-members: Members will be private, but setter methods will return 'this' like usual.
        nocamel:         Do not use CamelCase field accessors with beans.
        hashcode:        Generate quality hashCode methods.
        android_legacy:  Do not use java.io.IOException(throwable) (available for Android 2.3 and above).
        java5:           Generate Java 1.5 compliant code (includes android_legacy flag).
      javame (Java ME):
      js (Javascript):
        jquery:          Generate jQuery compatible code.
        node:            Generate node.js compatible code.
      ocaml (OCaml):
      perl (Perl):
      php (PHP):
        inlined:         Generate PHP inlined files
        server:          Generate PHP server stubs
        autoload:        Generate PHP with autoload
        oop:             Generate PHP with object oriented subclasses
        rest:            Generate PHP REST processors
        namespace:       Generate PHP namespaces as defined in PHP >= 5.3
      py (Python):
        new_style:       Generate new-style classes.
        twisted:         Generate Twisted-friendly RPC services.
        utf8strings:     Encode/decode strings using utf8 in the generated code.
        slots:           Generate code using slots for instance members.
        dynamic:         Generate dynamic code, less code generated but slower.
        dynbase=CLS      Derive generated classes from class CLS instead of TBase.
        dynexc=CLS       Derive generated exceptions from CLS instead of TExceptionBase.
        dynimport='from foo.bar import CLS'
                         Add an import line to generated code to find the dynbase class.
      rb (Ruby):
      st (Smalltalk):
      xsd (XSD):
    不多说了,敲下thrift命令,啥都支持啊。。。
  • msgpack  ☆
    ruby、python、perl、C/C++、php、C#、java、js....... 几乎没有不支持的,可以参考主页http://msgpack.org/
  • protocol buffer 
    和上述两个吓人的家伙比起来,有点拿不出手啊:java、c++ 和 python




你可能感兴趣的:(网络相关)