乐趣区

关于编码:数据密集型应用系统设计-数据编码和演化

sjmj《数据密集型利用零碎设计》– 数据编码和演变

前言

本章的前半部分提到的编码框架目前在 GO 畛域蛟龙得水,并且有不少成熟的产品诞生,如果是 GO 工作者必然会接触,如果仅仅是试图理解该畛域设计的一些技术架构,这一章更多的是扫盲和拓展眼界。

本章节的后半局部探讨的 RPC 和 SOAP,以及基于 WebService 服务跨语言通信服务,和 RPC 通信协议,然而 WebService 这货色当初用的人越来越少,反观微服务才是以后的支流。

尽管须要住的是尽管 HTTP/ 2 曾经进去不少年头的了,然而 RPC 仍然占有重要的比重,所以也是值得关注的,最为典型的当然是 Dubbo 框架。

为什么 HTTP2.0 都进去了,RPC 还在持续倒退呢?
因为 HTTP2.0 刚刚公布的时候,RPC 曾经具备相当的倒退,技术也绝对成熟,另外很多我的项目零碎架构曾经齐全搭建,换回 HTTP2 费劲不讨好,自身也没有必要,因为 RPC 协定没有特地大的缺点。并且领有不错的个性。

章节介绍

从历史的演变的角度来看,尽管过来呈现过许多尝试代替 HTTP 协定的 WEB 通信框架,然而市场总是须要稳固成熟的架构,这些新兴通信协议最终都默默黯淡在历史的长河之中。

零碎的演进除了数据结构和数据模型自身的演变之外,数据编码和数据之间的交互模式也在一直的进行演变,数据模式和格局扭转的时候,通常须要应用程序的对应扭转,而利用零碎的痛点如下:

  • 新版本的部署须要滚动降级(分阶段下线节点而后有序上线),通过这样的形式能够对于大规模降级的零碎能够实现不暂停降级。
  • 客户端应用程序须要依赖用户自行进行更新,或者应用强制更新伎俩强制降级。

这样的应用程序调整不可避免的带来关键性问题:前后兼容

什么是前后兼容?

向后兼容:较新的代码由旧代码编写的数据。

向前兼容:比拟旧的代码能够读取新编写的数据。

向后兼容不是难事,因为在原有的根底上扩大。向前兼容比拟难,须要对于旧代码疏忽新代码的增加。

尽管当初支流的传输构造是应用 JSON,然而在这一章节将会扩大更多的数据编码格局介绍,后面两种无需过多介绍,这一章节次要介绍了前面三种针对数据编码而存在相似中间件的框架:

  • JSON
  • XML
  • Protocol Buffer
  • Thrift
  • Avro

数据编码格局

数据表现形式无非两种:

  • 内存中数据保留对象,构造体、列表、数组、哈希表和树结构等等,传统的数据结构对于 CPU 高效拜访优化。
  • 数据写入文件通过网络发送,必须要编码为某种字节序列,然而因为一些虚构字节比方指针的存在所以和内存的表现形式有可能不一样。

术语问题,这里的编码其实就是指的“序列化”,然而序列化在不同的构造中意义不同,所以书中用了编码解释这一概念。

语言特定格局

通常有不少的编程语言反对把内存的对象编码为字节序列码,比方经典的Java.io.Serializable,Ruby 的 Marshal,Python 的 picle,还有一些第三方库比方 Kryo

然而语言的特定格局带来上面的一些问题:

  • 编码和特定语言绑定,无奈实现不同编程语言互通。
  • 复原数据的时候须要解码并且实例化对应实现类,序列化存在序列化攻打隐患,比方通过实例化异样对象的形式找到零碎的破绽攻打伎俩。
  • 简略疾速编码在编程语言经常导致前后兼容问题。
  • JAVA 的官网序列化低效被人诟病等。

JSON、XML 以及二进制

二进制编码

目前零碎较为支流的模式是 JSON,而过来 XML 也风行过一段时间,然而起初很快被更为轻便的 JSON 取代,JSON 最早是呈现在 JS 上的一种数据结构,起初被宽泛采纳在不同零碎之间的通信格局,至今仍然流行。

XML 和 JSON 的最大益处是应用字符串进行传输,并且 JSON 是 JS 内置的浏览器反对,具备很强的兼容性。

然而 XML 和 JSON 也暴露出不少问题:

  • 数字编码问题:JSON 中无奈辨别数字和碰巧是数字的字符串,尽管 JSON 能辨认出数字和字符串,然而无奈辨别数字的精度,也就是浮点数。
  • 针对浮点数问题,IEEE 754 双精度浮点数在 JS 上精度不佳。在推特中已经有精度失落的例子,2 的 53 次方会导致一部分数据失落而产生数据不精确问题,基本问题是应用了 64 位的数字去示意推文内容在 JS 中产生溢出!为了解决此问题推特最终应用了 拆分小数位和整数位,以及应用字符串代替数字的形式示意一个数字,避开了 JS 语言的缺点问题。
  • JSON 和 XML 对于文本反对较好,可浏览性很强,BASE64 编码之后能够解除数据传输失落的危险,然而与此同时也会带来数据大小收缩问题。
  • XML 和 JSON 都有模式可选反对,通常状况下大部分的编程语言能够通用编解码形式,然而对于不应用这两种编码格局的则须要本人编写。
  • CSV 没有模式,他只是介于二进制和文本之间的一种非凡状态,每一次数据改变都须要手动改变文件。

上面来探讨二进制编码问题。

二进制编码的劣势在于数据体积小并且传输快,然而二进制真的和 JSON 文本的差别很大么?

咱们能够看到上面的编码案例:

原始字符串内容如下,如果是传统的编码格局,上面的 JSON 字符串去掉空格须要 80 多个字节

在书中的案例中,通过二进制编码的数据仅仅比 JSON 编码格局放大了 10 几个字节,比方上面的编码格局,仅仅比下面的原始 JSON 缩短了 10 多个字节,是否意味着在较小文本传输的时候优化编码大小的性价比是很低的?

通过二进制编码框架解决之后,能够精简到 32 个字节甚至更小,约等于压缩了 50% 甚至更高的内容。

二进制编码框架定位

为了解决二进制编码的性能远不如文本 JSON 的问题,在数据编码和模式呈现了演进和深入研究。要更好地了解二进制编码框架,咱们须要理解他们的定位。

模式框架的设计了解根本和 TCP/IP 协定面对的问题相似,在差别不同的利用零碎之间如何实现对立格局通信,并且在不同利用系统升级之后能以最小的老本实现 向前兼容

为了更加透彻的理解 Thirft 以及一系列数据编码框架的设计定位,咱们来看看 Thrift 的设计思维:

Thrift软件栈 分层 从下向上 别离为:传输层 (Transport Layer)、 协定层 (Protocol Layer)、 解决层 (Processor Layer) 和服务层(Server Layer)。

  • 传输层 (Transport Layer):传输层负责间接从网络中 读取 写入 数据,它定义了具体的 网络传输协定 ;比如说TCP/IP 传输等。
  • 协定层 (Protocol Layer):协定层定义了 数据传输格局 ,负责网络传输数据的 序列化 反序列化 ;比如说 JSON、XML、 二进制数据 等。
  • 解决层 (Processor Layer):解决层是由具体的 IDL( 接口描述语言 )生成的,封装了具体的 底层网络传输 序列化形式,并委托给用户实现的 Handler 进行解决。
  • 服务层 (Server Layer):整合上述组件,提供具体的 网络线程 /IO 服务模型,造成最终的服务。

Thrift 和 Protocol Buffer

Apache Thrift 和 Protocol Buffer 基于雷同原理二进制编码,而 Protocol 最开始由谷歌开发,Thrift 最后由 Facbook 开发,前面被 Apach 引进并且成为顶级我的项目。(老接盘侠了)

Thrift 是 Facebook 于 2007 年开发的跨语言的 rpc 服框架,提供多语言的编译性能,并提供多种服务器工作模式;用户通过 Thrift 的 IDL(接口定义语言)来形容接口函数及数据类型,而后通过 Thrift 的编译环境生成各种语言类型的接口文件,用户能够依据本人的须要采纳不同的语言开发客户端代码和服务器端代码。

两者的共同点是都须要应用模式进行编码,所谓模式就是指如果通过语法来形容数据结构,须要依照指定的标准。

另外通过模式定义之后两者都能够通过代码生成器生成相干的对象代码,反对多种编程语言,利用代码生成器生成的代码能够实现对应的编码和解码操作。

在 Thirft 介绍一句话能够看到它最为根本的限度:
To generate the source from a thrift file run

有时候编码框架可能具备多种编码方式,比方 Thrift 分为 BinaryProtocol 和 CompareProtocol。

实际上 Thrift 还有 DenseProtocol,然而因为只能反对 C++ 所以这里并没有算进去。

首先是传统的 BinaryProtocol 形式,最终发现须要 59 个字节进行编码。

与下面的编码方式相似的是对于字段的内容进行了 ASCII 编码,区别是在字段名称上的编码方式存在区别,字段名会应用相似 Tag 的字段给字段名进行分类,这些数字次要用于模式定义。

应用 CompareProtocol,把雷同信息缩减到 34 个字节实现示意,次要区别是字段类型和标签号打包到单个字节当中,并且用变长整数实现。

Protocol Buffer 则只有一种编码方式,打包格局粗略看上去和 CompareProtocol 比拟像,只应用了 33 个字节示意反复的记录。

这样的区别来自于两个模式看待反复字段的前后兼容的解决形式不太一样。

须要留神后面设置的模式当中能够标记为 requiredoptional,这种标记对于编码没任何影响,然而如果 required 字段没有填充数据,则会抛出运行时异样,这对于大型零碎宏大数据系统查看数据格式是十分有帮忙的。

字段标签和模式演变

理解完格局定义,接着便是编码格局的模式演变。

通常一条编码记录是一组编码字段的拼接,数据格式应用标签号 + 数据类型(字符串或者整数)并以此作为编码援用,编码援用不会间接援用字段名称,不能随便的更改字段标签,因为这样解决容易导致编码内容生效。

如果字段没有设置字段值,则编码记录中将会间接疏忽

增加字段兼容

为了实现向前兼容性,字段字段名称能够随便更改,标签却不能随便更改。如果旧代码视图读取新代码的数据,如果程序视图读取新代码写入的数据,或者不能辨认的标记代码,能够通过类型正文告诉字段解析器跳过新增内容的解析。

而想要实现向后兼容性,因为新的标记号码总是能够被新代码浏览的,所以通常不会有太大问题。然而有一个细节是新增的字段不能是必填的,这有点相似给数据库新增必填字段,如果旧代码不进行改变则业务整个链路会解体,置信大家都有这样的体验。所以放弃向后兼容性初始化部署须要塞入默认值或者间接是选填字段。

删除字段兼容

删除字段的前后兼容刚好相同,向前兼容通常不会有多少影响,然而 向后兼容必须是删除非必填的字段,同时旧的标签号码须要永恒废除,因为应用齐全不同的数据类型标签,新标签笼罩旧标签号码会导致程序呈现奇怪景象。

字段标签扭转

如果是字段的删减仿佛问题并不会很大,应用标签在援用之间再套一层的形式能够解决这个问题。

然而如果是字段自身扭转要如何解决?比方把一个 32 位的整数转为 64 位的整数,如果是新构造的代码能够通过填充 0 的形式让数值对齐,然而如果是旧代码读取到新构造的代码,显然会呈现位截断的问题。

当初来看 ThriftProtocol Buffer 是如何解决这个问题的。

Protocol Buffer:利用字段 反复标记(repeated,示意可选之外的第三个选项),用于标记同一个字段标签总是反复的屡次呈现在记录当中。

通过设置可选字段为反复字段,读取旧代码的新代码能够看到多个元素的列表(前提是元素的确存在),新代码能够筛选合乎的值解决。而读取新代码的字段则只容许读取列表的最初一个元素。

这种解决计划有点相似 数据库的版本快照

Thrift:解决形式是应用列表对于字段标签参数化,尽管没有灵便的多版本变动,然而列表能够进行嵌套能够有更多灵便组合。

这种形式是相似用句柄的形式,利用“中间层”专门治理参数化标签。

Avro

同样是 Apach 的另一个二进制编码,AvroHadoop 的一个子项目,同样通过模式指定编码的一种数据结构,次要的防御方向有两条:

  • Avro IDL 人工编译。
  • JSON 利于机器读取

这里再一次用到之前的案例,Avro 对于同样的内容仅仅应用 32 个字节的编码。

这种二进制编码并没有显著的批示字段和数据类型,只是简略的连贯列值而已,字符串仅仅为长度前缀,只有整数应用了可变长度编码。

这样的灵便度不是依附数据结构自身撑持,而是换了一种思路,对于二进制数据的读写制订一套规定,在 Avro 中被叫做读写模式。

写模式和读模式

  • 写模式:指的是对于任意数据能够应用已知模式的所有版本编码,比方编译到应用程序的模式。
  • 读模式:须要依据模式解码某种数据的时候,冀望数据合乎某种模式。

和传统的编解码不一样,Avro 读写模式之间是能够进行互相转化的。

读写模式特点

最大的特点是 读写模式不须要完全一致,只须要放弃兼容即可,数据被解码读取的时候,通过比照查看读写模式,同时将写模式转为读模式进行兼容,而次要的限度是读写模式的转变须要合乎 Avro 的标准。

此外写模式和读模式的字段程序不一样也是没有问题的,因为模式解析会通过字段名称对于字段进行匹配,如果读模式碰到了呈现在写模式不存在读模式的字段就会执行 过滤 ,反过来如果读模式须要字段写模式没有提供会应用 默认值转化

模式演变规定

Avro 的模式演变规定象征,在向前兼容中把新版本的模式作为 write,把旧版本的模式设置为 reader,向后兼容则是新代码实现 reader,旧版本模式为 write。

这样实际上就是实现了新版本的写入会被新版本看到,然而旧版本不意识的数据就会被过滤掉。

Avro 为了放弃兼容性,只提供了默认值字段的增删权限,比方新增带有默认值的字段,应用新模式 reader 读取会应用默认值(如果读模式须要字段写模式没有提供会应用默认值转化),应用旧模式 write 则会间接过滤,并且只在新模式中能够看见新增默认值字段。

上面是模式演变的一个案例。

Avro 的前后兼容本质就是利 write 和 reader 这两个模式切换,利用新旧版本屏蔽的形式兼容代码。

Avro 除了这两个模式的特点之外,还有一种十分非凡的状况,对于 null 内容的解决 ,这和少数编程语言不同,如果 Avro 中申明 容许为 null 值,必须要是 联结类型

联结类型就像是上面这样的格局:

union {null, long, string}

ProtocolBuffThrift 都不太一样只有当 null 是联结分支的时候才容许作为默认值,此外它没有默认标签或者列表保护的形式可选(因为非凡数据格式设计导致的)。

write 模式抉择问题

Avro 还存在比拟纳闷的问题,如何抉择 reader 模式如何抉择 write 的版本?关键在于应用的上下文。

比方有很多记录的大文件:因为 Hadoop 中所有的记录都应用雷同编码,所以在这种上下文中只须要结尾包含 write 模式信息即可示意。

具备独自写入记录的数据库:不同的记录须要不同的模式和不同的版本解决,解决这种状况最简略的形式是每一个记录编码的结尾记录一个版本号,并且在数据库中保留一个模式版本列表。

reader 模式通过从记录的“数据库”中提取 write 模式实现对应的操作,例如 Espresso 就是这样工作的。

这个名字起的有点意思,翻译过去叫做稀释咖啡,在软件畛域是安卓的一款轻量级框架,具体理解具体能够看看这个网站:Espresso 基础知识  |  Android 开发者  |  Android Developers

网络连接发送记录,在建设连贯的时候建设模式建设版本,而后在生命周期当中实现工作,Avro RPC 的工作原理就是如此的。

动静生成模式

动静生成模式是 Avro 的另一项特点,动静生成对于模式兼容性更好,因为不带任何的标点符号,能够疾速实现不同模式之间的转化。

比方如果数据库模式转为 Avro 模式,只须要依据关系模式作为直达即可疾速实现转化,同时依据 write 和 read 模式的转变疾速实现被扭转字段的同步工作。

这意味着 Avro 的模式转化仿佛是其原生内容。如果应用 Thrift 或者 Protocol Buffers,则须要额定保护一套映射规定,同时保护模式生成器要特地小心谬误调配标签的问题。

因为动静生成模式是 Avro 的设计指标之一,所以它在这一块体现非常杰出。

代码生成和动静类型语言

传统思维上咱们认为编码框架比拟罕用于动态语言,对于动静类型编程语言实际上并没有太多的意义,然而 Avro 却走了一条非凡的路。

然而对于 Avro 的动静生成模式,应用固定格局框架代码反而是累赘,因为自身就能够通过动静模式实现模式转化。

Avro 的 动静生成模式 常常和 动静类型数据处理语言 联合应用,能够认为此编码框架自身就具备代码生成器的性能。

总之,不能带着刻板印象对待编码框架,有时候不同的设计思路,同样的赛道上会呈现非凡的产品,就好比图数据库走了以前的网络编程模型的老路,却开拓一条非凡的门路。

动静类型语言是指 在运行期间才去做数据类型查看的语言,动静类型语言的数据类型不是在编译阶段决定的,而是把类型绑定延后到了运行阶段。次要语言:Python、Ruby、Erlang、JavaScript、swift、PHP、Perl。

模式的长处

通过下面的一系列比照探讨,咱们发现模式比照 JSON 和 XML 格局相比,应用独特的框架设计以及简略易懂、可保护的特点,被宽泛的编程语言反对。

实际上模式框架自身的思维并不是什么新货色,ASN.l 在 1984 年首次被标准化的模式定义语言中能够看到相似的影子,ASN.I 自身也被用于 SSL 证书的二进制编码(DER)当中。

比照模式和 XML 以及 JSON,它们通常具备上面的特点:

  • 数据更加紧凑,甚至能够省略数据当中的字段名。
  • 模式自身具备文档化价值,可维护性要强于 XML 和 JSON。
  • 模式具备前后兼容性的查看,对于大零碎的降级保护这是十分有必要的。
  • 对于动态类型编程语言的用户来说,从模式生成代码的能力是有用的,它可能在编译时进行类型查看。

数据流模式

编码模式解决了不同架构之间的数据交换问题,为了实现这一指标,它须要具备简略的同时蕴含主动前后兼容的特色,所以归根结底模式是解决零碎变更艰难的问题。

流模式则探讨另一个话题,数据流动的过程,在软件系统生态架构中数据流动无非上面几种模式:

  • 通过 数据库(实际上仍然能够认为是中间件)。
  • 通过 异步服务调用
  • 通过 异步音讯 传递。

基于数据库流动

写模式对数据库编码,读模式对数据库解码。数据库通常须要保障向后兼容,否则前面的版本无奈读取之前的内容。

因为并发性问题不同的过程看到的数据状态可能具备差异,意味着数据库的数值能够被新版本写入,同时要兼容旧版本持续读取,阐明数据库也须要向前兼容性能。

基于数据库的流动 \ 问题和模式相似,新增一个字段容易导致数据读取的问题,现实状况下是旧版本代码放弃新版本字段的不变,哪怕齐全无法解释。

首先须要留神是新旧版本转化问题,有时候在应用程序读取新对象进行解码,之后在从新编码的过程中可能会遇到未知字段失落的问题。

为了解决下面提到的向前兼容问题,数据往往采纳的形式是把磁盘编码的所有数据填充空数值。

留神一些文档数据库自身会利用模式来实现向前兼容,比方 Linkedln 的文档数据库 Espresso 应用,Avro 进行存储,并反对的 Avro 的模式过渡规定。

归档存储

所谓的归档存储指的是对于数据库存储快照,因为应用快照对于数据进行复原,所以须要对于数据正本进行对立编码。

像 Avro 对象容器文件这样的对象容器文件非常适合,因为没有额定的模式字段保护,只须要利用框架自身的模式实现转化。

归档存储在本书第十章“批处理零碎”有更多探讨。

基于服务数据流:REST 和 RPC

REST 和 RPC 的概念

在零碎利用中 WEB 利用是最多的,而对于 WEB 的传输 API 包含(HTTP、URL、SSL/TLS、HTML)等,这些协定在过来受到宽泛认可,当初曾经成为大多人批准的规范。

通常状况下 HTTP 能够用作传输协定,然而在顶层实现的 API 是特定于应用程序的,客户端和服务器须要就 API 的细节达成统一。

RPC 的概念通常和微服务做比拟,古代的零碎设计更加偏向于细化分工和服务职责拆分,就算是简略的零碎也会依照分模块的形式进行职权拆分,独立部署和疾速演变是微服务的指标。

理论微服务也诞生这样的问题,不同的团队持有不同的微服务模块,这带来了 API 兼容以及数据编码的问题,这也是为什么编码框架和异步通信框架的诞生。

网络服务

针对 WEB 服务有两种风行的解决办法:RESTSOAP,这两个都不算是新货色。REST 是基于 HTTP 协定的设计而革新的另一种概念 和强化,SOAP 是基于 XML 的协定。

REST 的概念是利用 URL 标识资源,通过 HTTP 协定自身实现缓存管制,身份验证和内容类型协商。不同的是为资源定义更为显著的标记和界线。REST 准则所设计的 API 称为 RESTful Api。

SOAP 用于发送 API 申请,然而因为宏大简单的多重相干规范,这几年逐步被 REST 简略格调替换。SOAP WEB 服务的 API 叫做 WSDL。反对代码生成和拜访近程服务,然而同样针对动静编程语言的生成成果很弱。

只管 SOAP 及其各种扩大外表上是标准化的,然而不同厂商的实现之间的交互操作性往往存在一些问题,SOAP 尽管仍然被一些大厂商应用,然而针对小公司来说曾经不再受到欢送,而到了当初整个 WebService 的应用范畴也在一直放大。

须要留神这些探讨都是基于作者是外国人对于国外编程环境的探讨,到了国内则是齐全不同的另一番现象。

最初,ResultFul 的 API 生成工具目前较为支流的是应用 Swagger,Swagger 组件也是目前对外文档的一种优良格局,尽管注解和文本形容会让接口变得“简单”,然而的确非常好用。

近程调用 RPC

在过来许多的编程语言的近程办法调用大肆宣扬,然而它们多少都存在缺点或者一些显著的短板,比方:

  • JAVA 的 EJB 近程办法调用仅限于 JAVA;
  • 分布式组件对象模型 DCOM 实用于微软平台;
  • 申请代理体系 CORBA 不足前后兼容被放弃;

近程办法调用的思维从上世纪 70 年代就曾经呈现了,RPC 起初看起来很不便,但这种办法在基本上有显著缺点,网络申请与本地函数调用的微小差异:

  • 本地函数调用可控可保护。
  • 本地函数调用的后果根本能够预知,比方超时和过程解体都能够通过各种伎俩排查。
  • 每一次重试失败须要破费雷同的工夫持续重试,如果一个工作总是在将要实现的时候解体,不仅占用资源还容易导致系统的各种简单状况。
  • 本地函数能够借用内存实现对象的之间的高速传递。
  • 本地和近程调用端用不同语言实现,所以两头须要进行转化,或者借助编码框架实现前后兼容。

RPC 倒退

集体接触微服务比拟多,对于 RPC 理解不是很足,目前的认识是 不温不火 然而并没有齐全隐没。Thrift 和 Avro 带有 RPC 反对,gRPC 是应用 Protocol Buffers 的 RPC 实现,Finagle 也应用 Thrift , RestFul 应用 HTTP 上 的 JSON。

RPC 框架还在持续倒退,新一代框架更加明确 RPC 和本地函数调用。

  • Finagle 和 RestFul 应用 Futures 封装失败异步操作。
  • Futres 简化多项服务后果合并。
  • gRPC 反对流。

此外二进制编码格局也反对自定义的 RPC 协定,对于一些 REST 和 JSON 的协定具备更好的性能。RESTFUL 的设计格调当初看来反而有点脱裤子放屁,因为不过是包装了一层 HTTP 协定而已,仿佛 SOAP 的设计才是合乎 RPC 的定义,这个话题也常常被放上来进行探讨。

RPC 的数据编码和演变

因为是近程调用,波及不同服务之间的通信,必然波及到编码演进和前后兼容问题,而针对前后兼容问题,RPC 呈现制订了上面一些计划:

  • Thrift、gRPC (Protocol Buffers)和 Avro RPC 能够依据各自编码格局的兼容性规定解决。
  • SIAO XML 尽管是能够演变的,然而有陷阱。
  • RESTFul 应用 JSON 格局放弃兼容性。

此外对于 RESTful API,罕用的是在 URL 或 HTTP Accept 头中应用 版本号限定调用和兼容性放弃。另一种抉择是客户端申请的 API 版本存储服务器,同时提供多版本的接口治理调用性能。

异步音讯

RPC 和数据库之间的异步数据消息传递,是本章的最初一个话题,和 RPC 调用相似,客户端的申请同样低提早推送到另一个服务过程。音讯队列通过暂存音讯的形式,嫁接生成者和消费者。

和 RPC 相比的音讯队列有上面几个特点:

  • 音讯队列能够充当缓冲关照单方的解决能力。
  • 防止发送方须要晓得接管方 IP 和地址的问题。
  • 反对一个音讯发给多个接管方。
  • 逻辑上的发送方和接管方拆散。

音讯队列比较显著的问题是 消息传递是单向的,同时并不在意生产方是否进行回应。发送者发送之后通常会遗记它的存在。

音讯队列

音讯队列最早是由一些商用免费软件管制,之后才呈现各种开源风行软件 kafka、activeMQ、HornetQ、RabbitMQ 等。

同一个主题上能够绑定多个生产者和消费者,音讯队列不会强制任何数据类型,消息传递的元数据都是一些字节数据。

此外,主题通常只指定单向流,然而音讯自身会发给另一个主题和可能存在的多个消费者绑定。

音讯队列的另一显著劣势是前后兼容很容易实现,最大灵便的调整单方即可。音讯队列的内容在 12 章会持续进行论述。

分布式 Actor 框架

Actor 模型是 1973 年提出的一个分布式并发编程模式,在 Erlang 语言中失去广泛支持和利用,Erlang 是啥这里读者有可能忘了,简略关联一下:JVM 实现的次要编程语言。

Actor 是基于单过程的并发编程模型,所有的逻辑被封装到 Actor 而不是现成当中,每个 Actor 代表客户端的一个实体,也就是能够把每一个线程等同于一个过程对待。因为是单过程的设计,不须要线程问题,每个 Actor 都能够自在调度。

Actor 模型的计算形式与传统面向对象编程模型(Object-Oriented Programming,OOP)相似,一个对象接管到一个办法的调用申请(相似于一个音讯),从而去执行该办法

Actor 的最大特点是能够编程模型能够逾越多个节点扩大应用程序,无论发送和接管方是否在一个节点。换种说法是在不同的节点上音讯被通明封装为字节序列并且通过网络传递,同时在另一端解码。

分布式 Actor 实际上就是把音讯队列和 Actor 的编程模型绑定到单过程当中,能够简略看作是非凡版本的音讯队列,这样有一个益处是屏蔽了复杂性,然而害处是程序无奈细粒度的管制编程模型和函数,所有的规定都被 Actor 牢牢管制,此外 Actor 的显著缺点是具备前后兼容性的问题,因为新版本的节点可能被送到旧节点可能无奈失常工作。(这和 JAVA 的版本一样存在问题)

上面是 Actor 解决音讯编码的形式理论利用:

  1. 应用 Akka 形象让 JAVA 内置序列化,能够利用 Protocol Buffers 实现前后兼容。
  2. Orleans 应用自定义编码格局,须要部署新版本应用程序,同样能够反对序列化插件。
  3. 在 Erlang OTP 当中,然而很难对于记录模式更改。

Akka:

对并行程序的简略的高层的形象;异步非阻塞、高性能的事件驱动的编程模型;十分轻量的事件驱动解决(1G 内存可包容 270 万个 Actors)。

小结

本章内容量比拟宏大。第一个维度是探讨了数据格式的编码问题,以此产生了内存构造转为网络或磁盘字节流的办法,第二个维度是基于数据格式编码的前后兼容问题而诞生的不同框架比照:Thirft、Protocol Buffer、Avro,其中 Avro 花了很多的篇幅讲述,显然是本章的重点之一。

而最初一个维度则换到了另一面,从数据流动的形式看问题,和后面维度不同的是它如果把前两个看作设计一艘好船(数据格式)能停到不同的港口(服务),而数据流则是载着这些数据以何种模式流动,数据流的失常流动是目前的外围,古代是高可用为王。

在编码的细节外部能够看到哪怕是一个字节的变动都有可能带来性能影响,同时不同的设计理念间接影响零碎的部署形式。

在许多服务须要滚动降级的状况下,新版本须要顺次部署到几个节点,滚动降级是在不侵害旧版本失常运行下“不停机”降级零碎版本的通用伎俩,同时无效升高部署上线的危险。

滚动降级须要思考最大问题是数据格式的前后兼容问题,在微服务和模块更加细化的明天,这样的状况更加频繁呈现,哪怕是小我的项目也能够实现分布式部署,这样也带来了编码框架的前后兼容影响。

至于 Avro,这一章作者对它他吹特吹,然而集体并没有应用过相似的编码框架教训,对于各种体验笔记也只有个大略印象。

接着本章探讨了上面这些问题,首先是对于编码问题的探讨:

  • 特定语言只在特定的畛域实用,尽管 JVM 的野心是统合所有的编程语言,然而显然还有漫漫长路要走。
  • JSON、XML 是经典的通用兼容模式语言,然而因为宽泛应用的 JSON 诞生于 JS 在数字类型上存在显著纰漏。
  • Thirft、Protocol Buffers 和 Avro 遵循二进制编码的准则,对于数据进行前后兼容和高效编码,动态编程语言对于这样的框架非常受用,失去宽泛的编程语言的认可和反对。尤其是在 GO 的畛域大放异彩。

之后是数据流的探讨,数据流目前曾经十分成熟:

  • 数据库,因为存在“旧版本”数据读取的场景,通常应用非凡形式对于数据进行编解码,保证数据向前兼容读取。
  • RPC 以及 RESTFUL,RPC 仍然在蓬勃发展。
  • 音讯队列,高可用和高性能的代表产物,也是古代架构设计的三大马车之一(缓存、音讯队列、数据库),古代我的项目的三马车则是 微服务 音讯队列 定时工作
  • 分布式 Actor 框架,大数据中重要框架,如果是接触大数据的相干人员,Actor 框架显然占有重要的一席之地。

写在最初

这一章节均为实践和视线拓展,集体来看并不算是十分重要的章节(次要是没有理论接触),当然也写不出多少货色,感兴趣能够针对某一个话题深刻理解。

参考资料

Actor 分布式并行计算模型: The Actor Model for Concurrent Computation – 腾讯云开发者社区 - 腾讯云 (tencent.com)

深刻解析 actor 模型(一):actor 介绍及在游戏行业利用 – 知乎 (zhihu.com)

聊聊日常开发中,如何对接 WebService 协定?– 掘金 (juejin.cn)

退出移动版