一、MessagePack是什么

先看官方的定义:MessagePack是一种高效的二进制序列化格式。它允许您像JSON一样在多个语言之间交换数据。但是,它更快并且更小。

从官方定义中,可以有如下的结论:

  1. MessagePack是一个二进制序列化格式,因而它序列化的结果可以在多个语言间进行数据的交换。

  2. 从性能上讲,它要比json的序列化格式要好。

  3. 从结果大小上讲,它要比json的序列化结果要小。

但是官方并没有提MessagePack和google pb的对比,实际上从空间和时间两个方面对比,pb均要优于MessagePack,但pb相对MessagePack 的缺点是支持的语言种类比较少,需要编写专门的 .proto文件,使用上没有MessagePack方便。

二、MessagePack的主要概念

 

2.1 type system

类型体系是MessagePack的基础,也是MessagePack在序列化后比json占用空间小的关键。当前包含的type有如下几类:
  • Integer represents an integer
  • Nil represents nil
  • Boolean represents true or false
  • Float represents a IEEE 754 double precision floating point number including NaN and Infinity
  • Raw
    • String extending Raw type represents a UTF-8 string
    • Binary extending Raw type represents a byte array
  • Array represents a sequence of objects
  • Map represents key-value pairs of objects
  • Extension represents a tuple of type information and a byte array where type information is an integer whose meaning is defined by applications or MessagePack specification
    • Timestamp represents an instantaneous point on the time-line in the world that is independent from time zones or calendars. Maximum precision is nanoseconds.
 
这个类型体系将我们在代码开发中用到的数据格式进行了映射,并且通过Extension这个类型给使用者留出了自由扩充的空间,但由于表示形式的限制,当前Extension最多有127个。
 
每一种类型能够表示的范围可以查看MessagePack规范中的Limitation部分和Extension types部分。
 

2.2 formats

在MessagePack中一个value的组成格式是这样的:类型[长度][data]。下面列出几个示例,详细完整的描述请看附录中的MessagePack规范。
 

2.2.1常量型

比如对于null、true、false这三个值,在MessagePack会被固定的映射为如下的值。
format name
first byte (in binary)
first byte (in hex)
nil
11000000
0xc0
false
11000010
0xc2
true
11000011
0xc3

2.2.2 int型(包含有符号整数和无符号整数)

 
示例如下
 
  • 0xcc表示当前的值的类型是无符号整数并且长度不超过8个bit,具体的值内容需要通过后续8个bit位的内容来计算
  • 0xcd表示当前的值的类型是无符号整数并且长度不超过16个bit,具体的值内容需要通过后续16个bit位的内容来计算
  • 0xd0表示当前的值的类型是有符号整数并且长度不超过8个bit,具体的值内容需要通过后续8个bit位的内容来计算
  • 0xd1表示当前的值的类型是有符号整数并且长度不超过16个bit,具体的值内容需要通过后续16个bit位的内容来计算

uint 8 stores a 8-bit unsigned integer
+--------+--------+
| 0xcc |ZZZZZZZZ|
+--------+--------+ uint 16 stores a 16-bit big-endian unsigned integer
+--------+--------+--------+
| 0xcd |ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+ int 8 stores a 8-bit signed integer
+--------+--------+
| 0xd0 |ZZZZZZZZ|
+--------+--------+ int 16 stores a 16-bit big-endian signed integer
+--------+--------+--------+
| 0xd1 |ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+

2.2.3 字符串

 
  • 0xd9表示当前的值的类型是字符串并且长度不超过(2^8)-1个bytes ,具体的长度需要通过后续8个bit位的内容来计算,字符串的具体内容是后续长度的byte所表示的内容
  • 0xda表示当前的值的类型是字符串并且长度不超过(2^16)-1个bytes ,具体的长度需要通过后续16个bit位的内容来计算,字符串的具体内容是后续长度的byte所表示的内容
 
str 8 stores a byte array whose length is upto (2^8)-1 bytes:
+--------+--------+========+
| 0xd9 |YYYYYYYY| data |
+--------+--------+========+ str 16 stores a byte array whose length is upto (2^16)-1 bytes:
+--------+--------+--------+========+
| 0xda |ZZZZZZZZ|ZZZZZZZZ| data |
+--------+--------+--------+========+

  

2.2.4 数组

 
  • 0xdc表示当前的值的类型是数组并且长度不超过(2^16)-1个元素 ,具体的长度需要通过后续16个bit位(两个byte)的内容来计算,计算出来的值就是数组元素的个数
  • 0xdd表示当前的值的类型是数组并且长度不超过(2^32)-1个元素 ,具体的长度需要通过后续32个bit位(4个byte)的内容来计算,计算出来的值就是数组元素的个数
 
array 16 stores an array whose length is upto (2^16)-1 elements:
+--------+--------+--------+~~~~~~~~~~~~~~~~~+
| 0xdc |YYYYYYYY|YYYYYYYY| N objects |
+--------+--------+--------+~~~~~~~~~~~~~~~~~+ array 32 stores an array whose length is upto (2^32)-1 elements:
+--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
| 0xdd |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N objects |
+--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+

  

 

2.2.5 小结

 
 
 

2.3 Serialization:type to format conversion

 
source types
output format
Integer
int format family (positive fixint, negative fixint, int 8/16/32/64 or uint 8/16/32/64)
Nil
nil
Boolean
bool format family (false or true)
Float
float format family (float 32/64)
String
str format family (fixstr or str 8/16/32)
Binary
bin format family (bin 8/16/32)
Array
array format family (fixarray or array 16/32)
Map
map format family (fixmap or map 16/32)
Extension
ext format family (fixext or ext 8/16/32)
If an object can be represented in multiple possible output formats, serializers SHOULD use the format which represents the data in the smallest number of bytes.
 

2.4 Deserialization: format to type conversion

source formats
output type
positive fixint, negative fixint, int 8/16/32/64 and uint 8/16/32/64
Integer
nil
Nil
false and true
Boolean
float 32/64
Float
fixstr and str 8/16/32
String
bin 8/16/32
Binary
fixarray and array 16/32
Array
fixmap map 16/32
Map
fixext and ext 8/16/32
Extension
 

三、为什么MessagePack比json序列化使用的字节流更少

 

3.1 直观对比

可以通过下图的两张图简单进行下对比,第一张图是同一个数据类型的内容用json和messagepack序列化的结果。
 
 
 
从第二张图可以明显看到messagepack要比json占用的空间更少。
 

3.2 序列化结果只有value且value进行了专属映射

 
这张图是MessagePack官网上的,用来进行json和MessagePack序列化结果的对比,实际情况是否确实如此呢?
我本地使用的msgpack-0.6.12版本。
代码如下:
 
public class MessagePackSerializationCompareJson {

    @Message // Annotation
public static class MyMessage {
// public fields are serialized.
public boolean compact;
public int schema; public String toString() {
return "compact:"+compact+";schema:"+schema;
} }
/**
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//初始化一个对象
MyMessage src = new MyMessage();
src.compact = true;
src.schema=0; //利用MessagePack进行序列化
MessagePack msgpack = new MessagePack();
// Serialize
byte[] bytes = msgpack.write(src);
System.out.println("msgpack result length:"+bytes.length); //利用json进行序列化
String jsonResult = JSONObject.toJSON(src).toString();
System.out.println("json result length:"+jsonResult.getBytes().length); } }

  

 
运行结果如下:
 
json result length:27
msgpack result length:3

  

 
json序列化的结果是27,和官网图片中的结果相同。但MessagePack的序列化结果是3,要比官网中的数字小很多。
按照上面图片的解释应当是:
  • 第一个byte是82,表示序列化后的结果有两个元素
  • 第二个byte是c3,表示第一个元素的值是true
  • 第三个byte是00,表示第二个元素的值是0
 
为了验证我们的推测,我们可以在MyMessage类中再添加一个boolean类型的属性,但不给这个属性赋值,按照java的规范,这个属性的值就是false,按照MessagePack的规范,就会被转为一个byte的c2,这样msgpack序列化后的长度值就是4.  而json序列化的增加值要增加不少,是属性名称的长度+5(false的长度)+4(要增加两个双引号,一个逗号,一个冒号),如果属性名称长度是4,则一共会增加13个byte,总长度就是40.
public class MessagePackSerializationCompareJson {

    @Message // Annotation
public static class MyMessage {
// public fields are serialized.
public boolean compact;
public int schema;
public boolean link; public String toString() {
return "compact:"+compact+";schema:"+schema+";link:"+link;
} }
/**
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//初始化一个对象
MyMessage src = new MyMessage();
src.compact = true;
src.schema=0; //利用json进行序列化
String jsonResult = JSONObject.toJSON(src).toString();
System.out.println("json result length:"+jsonResult.getBytes().length); //利用MessagePack进行序列化
MessagePack msgpack = new MessagePack();
// Serialize
byte[] bytes = msgpack.write(src);
System.out.println("msgpack result length:"+bytes.length); } }

  

 
上面代码的执行的结果也符合猜测:
 
json result length:40
msgpack result length:4

  

 
从这个数字上看,MessagePack明显优于json,特别是在属性多的情况下差距会更大。即使json中把key去掉,序列化后的结果也要比MessagePack占用的空间大。
 

3.2 序列化对象的属性顺序不能变动

3.1分析了MessagePack序列化的结果中只包含了value,而不包含key。因而在进行反序列化需要保证类中属性的顺序必须保证完全一致,否则就会出错:
如果两个属性的类型一致,可以反序列化,但是值发生错乱。
如果两个属性的类型不一致,会抛出类型不匹配异常。
 

3.2.1 顺序不同,类型相同

 
public class SimpleMessagePackPractice {

    @Message // Annotation
public static class MyMessage {
// public fields are serialized.
public boolean compact;
public boolean link; public String toString() {
return "link:" + link + ";compact:" + compact;
}
} @Message // Annotation
public static class MyMessage2 {
// public fields are serialized.
public boolean link;
public boolean compact; public String toString() {
return "link:" + link + ";compact:" + compact;
}
} /**
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException { //初始化一个对象
MyMessage src = new MyMessage();
src.compact = true;
src.link = false; //利用MessagePack进行序列化
MessagePack msgpack = new MessagePack();
// Serialize
byte[] bytes = msgpack.write(src); //利用MessagePack进行反序列化
MyMessage2 dst = msgpack.read(bytes, MyMessage2.class);
System.out.println("msgpack 原始数据:" + src);
System.out.println("msgpack 反序列化:" + dst); } }

  

 
上述代码的执行结果如下:
msgpack 原始数据:link:false;compact:true
msgpack 反序列化:link:true;compact:false
 

3.2.1 顺序不同,类型不同

 
public class SimpleMessagePackPractice {

    @Message // Annotation
public static class MyMessage {
// public fields are serialized.
public boolean compact;
public String link; public String toString() {
return "link:" + link + ";compact:" + compact;
}
} @Message // Annotation
public static class MyMessage2 {
// public fields are serialized.
public String link;
public boolean compact; public String toString() {
return "link:" + link + ";compact:" + compact;
}
} /**
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException { //初始化一个对象
MyMessage src = new MyMessage();
src.compact = true;
src.link = "www.baidu.com"; //利用MessagePack进行序列化
MessagePack msgpack = new MessagePack();
// Serialize
byte[] bytes = msgpack.write(src); //利用MessagePack进行反序列化
MyMessage2 dst = msgpack.read(bytes, MyMessage2.class);
System.out.println("msgpack 原始数据:" + src);
System.out.println("msgpack 反序列化:" + dst); } }

  

 
执行结果:
 
Exception in thread "main" org.msgpack.MessageTypeException: Expected raw value, but got boolean
at org.msgpack.unpacker.Accept.acceptBoolean(Accept.java:33)
at org.msgpack.unpacker.MessagePackUnpacker.readOneWithoutStackLarge(MessagePackUnpacker.java:154)
at org.msgpack.unpacker.MessagePackUnpacker.readOneWithoutStack(MessagePackUnpacker.java:139)
at org.msgpack.unpacker.MessagePackUnpacker.readOne(MessagePackUnpacker.java:73)
at org.msgpack.unpacker.MessagePackUnpacker.readString(MessagePackUnpacker.java:502)
at org.msgpack.template.StringTemplate.read(StringTemplate.java:46)
at org.msgpack.template.StringTemplate.read(StringTemplate.java:25)
at org.msgpack.template.AbstractTemplate.read(AbstractTemplate.java:31)
at com.my.msgpack.SimpleMessagePackPractice$MyMessage2_$$_Template_1305193908_1.read(SimpleMessagePackPractice$MyMessage2_$$_Template_1305193908_1.java)
at org.msgpack.template.AbstractTemplate.read(AbstractTemplate.java:31)
at org.msgpack.MessagePack.read(MessagePack.java:388)
at org.msgpack.MessagePack.read(MessagePack.java:371)
at com.my.msgpack.SimpleMessagePackPractice.main(SimpleMessagePackPractice.java:61)

  

 

参考资料

MessagePack简析的更多相关文章

  1. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  2. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  3. RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  4. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  5. PHP的错误报错级别设置原理简析

    原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...

  6. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  7. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  8. Java Annotation 及几个常用开源项目注解原理简析

    PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...

  9. 【ACM/ICPC2013】POJ基础图论题简析(一)

    前言:昨天contest4的惨败经历让我懂得要想在ACM领域拿到好成绩,必须要真正的下苦功夫,不能再浪了!暑假还有一半,还有时间!今天找了POJ的分类题库,做了简单题目类型中的图论专题,还剩下二分图和 ...

随机推荐

  1. 分布式监控系统--zabbix

    1Zabbix简介 Zabbix 是一个企业级的分布式开源监控方案. 2.监控系统架构 C/S架构 客户端/服务器端,这种架构适合规模较小,处于同一地域的环境 C/P/S 客户端/代理端/服务器端/, ...

  2. 好的Qt学习资料

    1.青春不老,奋斗不止!---CSDN博客地址http://blog.csdn.net/liang19890820:

  3. 国寿e店/人寿云参会云助理,不去公司就能刷脸考勤打卡?

    自从2017年3月平安保险公司实行E行销打卡考勤以来,保险增员迅猛增加,保险业绩也随之水涨船高.年底开始中国人寿保险也陆续开始实行app考勤,有些需要连接公司指定WiFi,或在指定地点方可打卡考勤.不 ...

  4. 函数iconv_substr和mb_substr

    二个函数iconv_substr和mb_substr,均可以在当前字符下进行字符串截取,以达到中文字符截取的不乱码. 应该如何选择呢? 1.iconv库在某些操作系统上可能运行不正确,需要安装GNU扩 ...

  5. Mac下用SSH连接远程Linux或Mac服务器

    1.打开Mac终端 2.切换到root登录 输入命令:sudo -i,然后输入本机密码 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px &qu ...

  6. Java接口和抽象类的理解

    接口和抽象类的相同之处就是 都会有抽象方法 抽象方法就是一个没有方法体 等待继承的子类完成的方法 然而接口比较严格 它的方法必须是抽象方法且是公开的 抽象类 可以有自己的属性 和 实体方法 首相用面向 ...

  7. PUTTY无法远程连接服务器故障解决[转]

    对于一个刚刚了解putty工具的新手来说,在putty工具使用中有时出现了问题而无法解决.今天就来介绍怎么解决putty无法远程连接服务器的故障. 用putty远程连接服务器时,提示错误 server ...

  8. 用SecureCRT来上传和下载文件

    用SSH管理linux服务器时经常需要远程与本地之间交互文件.而直接用SecureCRT自带的上传下载功能无疑是最方便的,SecureCRT下的文件传输协议有ASCII.Xmodem.Zmodem. ...

  9. JavaWeb项目架构之Redis分布式日志队列

    架构.分布式.日志队列,标题自己都看着唬人,其实就是一个日志收集的功能,只不过中间加了一个Redis做消息队列罢了. 前言 为什么需要消息队列? 当系统中出现"生产"和" ...

  10. yaml 格式

    来源:http://www.ruanyifeng.com/blog/2016/07/yaml.html?f=tt 1.YAML是一种通用的数据串行格式 2.基本语法规则: 大小写敏感 使用缩进表示层级 ...