做为一个程序猿,对造轮子这事情能够说是情有独钟,几乎程序猿心里都存在一个梦想是去将开源的技术都实现一遍,全部从本篇开始,我会开一个造轮子系列。
首先,看看这个,想必你们对下面这种简历看得比较多了吧?
-
精通JAVA,Python,熟练掌握C++
-
精通Redis,Memcached,Mysql
-
精通Nginx配置,模块开发
-
精通Kafka,ActiveMQ 等消息队列
-
精一般用数据结构和算法
-
精通网络编程,多线程编程技术,高性能服务器技术
-
精通tcp/ip协议栈,熟悉内核网络子系统代码
-
精通nginx代码及模块开发
上面每一条都涉及好多轮子,每个都是精通,若是真能作到。那这我的能够说是码农中的战斗机。
那咱们如今目标就是去作这个战斗机。而这个方法,就是本身去造轮子,造的目的不是为了在项目中使用本身造的轮子,而是为了去了解轮子的构造,而后本身动手去体会造轮子的过程。
后端的轮子们
提及后端的轮子们,你们均可以说出一大串来,咱们大体来数一数啊。
-
抗在最前面的:LVS,F5,HAProxy这类负载均衡
-
接下来有Nginx,Apache,Lighttpd这类Http服务
-
http服务后则是各类容器,部署着咱们的业务逻辑
-
存储这边有Redis,Memcached这一类KV存储器和缓存系统
-
若是是多机部署,确定还有Kafka,ActiveMQ这种负责解耦的消息队列
-
为了实现集群通讯,确定少不了Thrift这种RPC框架和Protobuf这种序列化技术
-
再高端点,到了分布式领域了,就是更多的轮子了。。zookeeper、raft等等
-
还有大数据系列hadoop。spark。。。。。
本文先开始咱们的第一个轮子,服务器通讯须要用的数据序列化反序列技术:protobuf。
讲基础前,先附上一张极客时间中的一个技术须要从哪些角度来说的图片,本文也会尽量从这些个方面来说。
-
应用角度
-
-
问题:”干什么用“
-
技术规范:”怎么用“
-
最佳实践:”怎么能用好“
-
市场应用趋势:“谁用,用在哪”
-
设计角度
-
-
目标:“作到什么”
-
实现原理:“怎么作到”
-
优劣局限:“作得怎么样”
-
演进趋势:“将来如何”
Protocol buffers
应用角度
干什么用的?
序列化数据用的?何时须要序列化?当数据须要存储或者网络传输的时候。为何呢?
在存储或者传输的时候,咱们能看到都是一些二进制数据,即010101……的bit。
假设咱们看的一个对象是:
Struct myData
{ Int a; Int b;}data = myData { a:1, b:2,}
那咱们在网络上收到是一个字节流,咱们为了可以从字节流中恢复出数据 data,咱们要作的工做是:css
-
正确识别出data在字节流中开始和结束的位置
-
识别出a的值,识别出b的值
一个可能的字节流协议就是:
刚开始是8bit标明后续数据是哪一个结构,而后是两个4字节表示a和b。
注意!!!!!上面作出上面这个假设,有几点是咱们默认的:
-
咱们认为字节流开始先是8bit标明是哪一个数据结构,此处是myData(ps:不一样结构之间编号不一样)
-
最多可以支持2^8种结构
-
通信双方都须要拿到myData的定义文件
如下是一个上面实现的示例代码见GitHub。
能够看到在go中很容易就实现了咱们的一个数据结构的序列化反序列化。
设计角度
作到什么?
上面咱们只是实现了一个最简单的实现了一个序列化方法,下面咱们来看若是要实现一个生产环境中的序列化协议,须要作到哪几点。
-
通用性:语言、平台无关
-
高性能:序列化和反序列化都要快
-
高压缩:序列化后数据尽量小,小就意味着网络传输数据少
-
兼容性:数据结构改变了,也可以支持新老版本
下面咱们带着这些目标来从应用角度来看下”怎么用“
讲完使用下面就是设计角度:如何作到的?
首先咱们看前文咱们本身实现的简易序列化、反序列方法,咱们对每一个结构进行编码,而后在头部写上该结构是啥,而后后面就是结构中每一个字段的具体值,接着咱们写了序列化器的目标是:
简易、高效、兼容,下面咱们从这几个方面来看protobuf有什么改进的地方。
先来个小插曲,protobuf全称是Protocol buffers,其中buffers点名了使用上很是重要的一个点,即咱们在反序列化的一段二进制数据的时候,咱们要将其先读入到buffer中,而后再识别出单个数据结构的开头和结尾,最后才能正确的反序列化出来。
前面咱们设计的时候,还在头部对数据结构进行了编码,那为了可以作到更高效,数据更小,咱们是否能够把这个头也去掉呢?
固然是能够的,因而咱们的结构就变为了只有对应的字段值了,这么作的一个前提是:!!咱们必须清楚知道咱们识别出来二进制数据,其对应的具体是哪一个数据结构。!!
如今咱们去掉告终构描述,那怎么可以作到更小呢?譬如一样是int64,1 和 1<<32不必都用8字节来表示,譬如咱们能够先对数据类型作一个编码,而后紧跟着后续使用的bit,再跟着真正的数据。
每一个部分分别用几个bit来表示呢?
那一个解决方法就是:咱们去掉有效字节的字段,咱们把这个是否有更多数据信息编码进数据自己中,示意图以下:
解决了编码int类型的字段后,若是遇到string类型呢?这种类型首先也是数据类型描述,接着应该要是编码后续有效字节,这是一个int,这能够采用上面的方法来编码,再跟着就是有效数据了。
目前protobuf支持的数据类型
上面有个主意的对于有符号数,咱们要单独处理下,由于有符号数的最高位是经过0,1来表示正负的,可是上面编码中最高位却用来表示是否有后续数据了,因此咱们要经过ZigZag 编码将有符号转换为无符号。
原理很简单,就是经过下面的编码方式: nginx
解决了编码后,咱们来看最后一个问题:兼容性。
若是咱们改变了数据结构:新增或者删除了字段怎么办???
这个也好解决,那咱们就给全部字段加上编号,经过字段来表示这个数据是结构体中哪一个字段,protobuf在设计上编码方式以下:
从上图中tag的编码,咱们能够发现,若是field_num > 16的话,tag编码出来会使用超过1字节,全部对于咱们常用的字段,建议将其编码到0-15,减小tag位数。
实现原理小结
下面咱们对上面介绍的实现原理作个小结
高效:变长编码,非自描述
兼容:对filed进行编码
下面咱们再从应用角度讲下 protobuf 的最佳实践和市场应用趋势。
protobuf刚开始设计出来主要是为了解决接口兼容性问题,目前是主要用在内部服务之间RPC调用和传递数据,目前时候用protobuf做为序列化的rpc框架有gRPC,这也是后续咱们会介绍的。
最后咱们从设计角度来看下protobuf的优劣局限和演进趋势。
优势
protobuf最大的优势就是先后兼容性,已经部署的使用老数据格式的服务,即便接口升级了也能够继续使用,而后就是性能,固然是快了,具体能够看
序列化 / 反序列化性能
缺点
相比较json来讲,可读性差,特别是在调试阶段,相比较json咱们没法清晰的知道输入和输出。
最后
总结下本文
-
Protobuf设计之初主要是为了解决兼容性问题,实现上是对每一个字段进行编号,当遇到不存在的字段时,则忽略掉。
-
Protobuf为了可以作到高性能,在编码时采用了Tag - Value (Tag - Length - Value)的方式,使序列化后的数据更紧凑
-
Protobuf为了可以作到高性能,丢弃了自描述信息,即咱们只拿到数据,而没有拿到proto文件,咱们是没法反序列数据的
-
Protobuf提供了一套编译工具,可以生成不一样语言的数据序列化、反序列化方法,极大的提升了易用性
预告
下一篇咱们会介绍grpc,来看下rpc框架中是怎么使用protobuf的。