序列化简述

来源:互联网 发布:在线汉语词典 知乎 编辑:程序博客网 时间:2024/06/05 00:38

序列化方法的不同,主要在于客户端和服务端对信息的传递和共享的方式的不同。

信息描述的所有要素

以protobuf 描述的 person为例:

message Person {  required string name = 1;  optional int32 id = 2;  repeated string email = 3;}

序列化的信息有

  1. 有哪些数据段。
  2. 数据段是否必须和是否重复。
  3. 每个数据段是什么类型,决定了长度(或者判断长度的方法,比如string没有固定长度)
  4. 每个数据段的名字,都是字符串,如 “name”, “id”, “email“
  5. 数据段的id,这里以123标明,用于在二进制流中标明对应哪个字段。

事实上名字也可以标明字段,如果传输过程中字段的名字标注了就不需要id了,AMF编码就是如此。但是名字是字符串占用的空间比较大,所以有些序列化方法就采用id。

其实如果每个字段都是必须的话,只要按顺序解析就行了,不需要id。

如果有optionel字段,为null的时候采用一个特殊的编码标志,按顺序解析,也不需要id。


包含信息的3个地方

整个序列化过程,有3个含有大部分信息的格式,如同面向对象编程把信息分为”类“和”对象“,这3者也可以做类似划分。

  1. 对象在对应语言中的类和对象。(类1,对象1)
  2. 为了保持平台语言无关性,独立出来描述信息组织方式的语言,可以称为scheama,如protobuf中的.proto文件。(只有类2信息,没有对象信息)
  3. 序列化后的用于传输或保存的二进制流或文本(对象3),有3种格式。
序列化只需要对象1和对象3之间能相互转换。

序列化的3种格式

主要分类依据就是 数据、长度、字段名、类型存放在哪里。其中数据和长度这两者是必然在序列化数据中的,所以就看字段名和类型信息放哪,使解码端可以获取。

  1. 每个字段直接存入序列化流,只有长度和数据,字段名和类型全部硬编码。
  2. 像protobuf,包含每个字段指明id,类型信息只指出大类。
  3. AMF或XML,包含每个字段的name以及类型信息,相当于包含了(类3)的信息。


发现protobuf 也有Self-describing Messages,通过FileDescriptorSet +DynamicMessage 实现。



序列化的3种方法

序列化一般来说有4种方式

  1.  客户端和服务端都由用户,根据自己所知道的信息格式,编写序列化代码。
  2. 如protobuf一样使用工具从描述语言获取信息格式,编译为对应语言的序列化代码。
  3. 如同AMF编码,包含了名字、值、类型所有信息,自己就包含了信息格式,序列化代码可以统一处理,不需要人为处理每种类型或RPC包。
  4. 动态解析.proto来进行序列化的,这种方式生成的对象没有对应的类,相当于AMF的Objec或lua的table,c++中对应的就是存KV的hashtable。


2和1本质上是一样的,编码的过程中,都去掉了字段的名字和类型,信息是不完整的,在客户端和服务端需要另外的描述文件(c++中就是类声明文件)来完善信息。

编解码过程是硬编码的,如果有所改动就需要重新编写序列化代码(或编译为新的序列化代码)。

而3如AMF编码的如果发送一个Object,里面的的组织格式可以任意改动。

protobuf 的Self-describing Messages应该是同3的。


第一种对于大的项目一般不适用,比较一下剩余3种的优劣

1.protobuf

编解码快,类型还有名字都是硬编码在代码中。

序列化空间小,没有类型信息,name用id替代

程序中对象空间占用小,因为name也就是K是在类定义文件中,而不是在对象中。

修改格式,需要先修改.proto,再编译为对应语言的类定义文件,再重新编译程序。

2.amf

编解码中,类型名字信息在序列化数据中,先要解析这两者再去取V。

序列化空间大,需要包含name还有类型信息。

程序中对象空间占用大,都是hashtable存的对象,同时包含K,V。

修改格式方便,没有任何额外的定义需要修改。

3.动态通过.proto编解码

编解码慢,类型名字信息在.proto中,要先读入.proto,再解析.proto中的类型和名字,最后才去取序列化中的V。可以预先读入并解析所有.proto,这样速度也较快。

序列化空间小,同1。

程序中对象空间占用大,同2。

修改格式,需要修改.proto,但不需要编译为类定义文件和重新编译程序。

现有方案

XML

json

AMF

Avro

使用json描述scheama,不同于protobuf将scheama生成代码。Avro并不需要生成代码,模式和数据存放在一起,而模式使得整个数据的处理过程并不生成代码、静态数据类型等等。当在RPC中使用Avro时,服务器和客户端可以在握手连接时交换模式。服务器和客户端有着彼此全部的模式,因此相同命名字段、缺失字段和多余字段等信息之间通信中需要解决的一致性问题就可以容易解决。

个人感觉类似protobuf 的Self-describing Messages,scheama数据不是像AMF直接编入序列化中,那么就是动态解析scheama结合数据了。


thrift

protobuf


使用

采用何种方案应该视情况而定。我们游戏由于是Flash游戏,所以直接采用了AMF编码,这样命令消息发送很方便,不用静态编译,也不用Schema,可以直接修改命令消息格式。

个人认为在游戏这一应用参景,可以在大部分情况下使用类型和字段名都包含在序列化中 的编码方式。

xml、json、amf,这3个都是包括KV的。xml最大,因为除了每条消息两个key,还带一堆尖括号。json只有一个key。amf对于大量重复的应该有优化。

前两者好处是文本编辑,而amf是二进制的。


如果有网络或性能压力,可以针对两种情况优化,这些可以采用序列化方法中的方法1直接硬解码,或者采用方法2类protobuf的方案。

1.含信息量大的命令消息,比如初始人物所有数据传到客户端。

2.频率高的消息,比如人物行走位置消息。

需要给这些特殊命令消息,和上面采用AMF等更耗资源的通用包作为平级,单独写一个1级包格式。通用包内再定义一种子的2级格式。

1级包格式不要采用字符串命名,而应该采用最省空间的数字来作为id,如通用消息id1,人物数据id2,人物行走id3。

特殊命令如果采用硬解码,如位置消息:1.包长度。2.id为3。3.x。4.y。5.角色id。


Flash务实主义(八)——减少数据传输量 这篇也主要谈的序列化,不过只谈了道具采用xml,难道还游戏中每个不同数据采用不同格式?

原创粉丝点击