IAP - 消息结构 (jenkov.com)
如果我们能够将所有点今天一个行业的累积的知识来看当今的云计算平台是什么样子,但是随着新技术的迎头而上,过去的协议和技术能够适应吗?而且这些云平台能够支持下一代的互联网-物联网(IoE)吗?
对于 Jenkov Aps 和 Worpcloud 有限公司来说,我们也抱着相同的问题,但是我们将此当作一种激励,并在去年的早些时候联合起来开发了一个全新的项目: Vstack.co 。在过去的 2015 年,我们分析了当前的大部分的技术栈,从高层次的架构视野,到具体的实际技术如数据库、查询语言、消息队列、备份解决方案、分布式计算模型、乃至于网络协议。
当然,分析和验证概念是一个需要持续进行下去的工作,但是通过过去一年的努力,我们还是有了一些个阶段性的结果的,即从现有的云平台的技术栈看到了明显的不同。事实上,我们发现今天的互联网技术有多个是可以用更好的技术取代的。
我们的项目的最终结果是否能满足我们的期望,现在下结论还言之过早,因为该平台还没有完全开发完毕。在开发的过程,我们更改过多次设计,并从中学习到了很多,但是,目前的设计算是稳定的了,我们认为是时候拿出来和开发者们一起讨论下了。
我们无法在一篇文章中讲所有的架构都描述清楚,也无法做到讨论到所有的细节,因为甚至有些部分仅内部可见。因此,本文仅专注于我们的架构中的一个核心概念,IAP,即我们所建议的替代 HTTP 的协议!
IAP-互联网应用程序协议
互联网应用协议(IAP)意在为 Web 3.0 提供一个通用的网络协议。我们认为互联网应用协议的适当标准化对于应用程序来说就像是 USB 设备对于 PC 和外设这样的结果,设备和应用所链接的网络可通过链接到互联网来进行互动。
IAP 还尚在开发中,但核心部分已经可以正常工作了,IAP 规范目前的版本可以从这里找到。
IAP 实现了 HTTP 在 1.1 版本中所忽略掉的一些用例,虽然确凿无疑的是 HTTP 2.0 和 WebSocket 也实现了 HTTP 1.1 所没有实现的,但我们依然认为有必要再进行补充,关于具体原因,我们曾经在博客专门撰文解释过:为什么 HTTP 2.0 和 WebSocket 依旧不够用?
IAP 是基于自由流动的消息的协议。通信的节点交换信息,一如 HTTP 的请求和响应机制,然后,IAP 对每个消息必须都得响应没有要求,作为自由流动的协议,IAP 仅指定交换消息的节点。消息可以作为通信节点看到适合于通信的目的在网络连接的两个方向自由流动。我们在教程: IAP 消息流对核心的消息流有更为详细的描述。
为了能够推动 IAP 的顺利完成,我们以为已经到了在社区征求大家的讨论关于替代 HTTP 的时机了,并希望能够获得一些解决方案的建议,实际上,本文将会聚焦于 ION,在 IAP 中使用的二进制对象符号,我们之所以这么做是基于下面三个原因:
首先,ION 的规范已经非常的接近稳定版了,相对于 IAP 的规范则还差很远,因此从 ION 入手讨论更有实际意义。
第二,ION 可以独立于 IAP 之外使用,这也就是说用户可以基于 HTTP 来使用 ION,相比于 HTTP/JSON 的组合性能会有所提升,当然这里指的是不是浏览器的通信。举个实际的例子,如移动 app 和用户的后端的通信,或者是后端对后端对通信。
最后,ION 对于 IAP 最终的使用会有很大的影响,所以在 IAP 规范完成之前,理解了 ION 这样基本的 IAP 消息结构会为用户带来足够的理由和信心:对 IAP 最终的期待。
ION 介绍
正如我们在上文提到过的,IAP 是一种基于消息的网络协议。所有的 IAP 消息均为被编码为一种称之为 ION 的二进制格式中;即 IAP 对象符号的缩写。使用二进制的理由是相比于文本数据格式如 XML 和 JSON ,能够携带更多的消息和更加快速的解析速度。
ION 是一种 TLV (类型、长度、值)的格式。每个 ION 包都会包含它都类型、长度和值,我们在这里规范了 ION 编码的更多细节。
对 ION 对编码和对二进制表示的格式 CBOR 和 MessagePack 的编码,但是 ION 在某些方面和它们这些完全是背道而驰的。首先,无论是 CBOR 还是 MessagePack 均是古老的设计为 JSON 的二进制表示,只是增加了携带裸的二进制数据的能力(比如:文件)。
ION 的被设计为通用目的数据格式,用于建模以下常见的数据结构:
- 原始字节(文件)
- 二进制原语(布尔、整型、浮点、UTF-8 字符、UTC 日期)
- 独立值的流(未绑定)
- 独立值的数组(绑定)
- 映射(键值对)
- 对象
- 循环引用对象图
- 表
这些结构是可以相互嵌套的,例如,你可以通过嵌套表来建模一个紧凑的对象树。这些基本的数据结构可以建模为 JSON、XML、CSV 或者是裸的二进制数据,而且你可以实际的将至转换为 JSON、XML、或 CSV 为 ION,并且也还能够再转换回去。
表
在 ION 和 MessagePack/CBOR 之间最为显著的区别的就是表格式数据的紧凑表示,例如:
- 同一类型的对象数组
- 数据库表
- CVS 文件
能够代表表格式数据的紧凑的好处就是性能、带宽和内存的使用情况。我们的测量表明,ION 表的数据量往往比序列化到 JSON 对象数组相同的数据小 50-80%,同样的,典型的 ION 表要比相应的 MessagePack 或 CBOR 编码的数据要小上 25%~50%。多数的服务发送和接收列表的结果都是从网络上进行的,所以这在常见的情形下能够会影响到性能。
读取和写入 ION 表的速度 要比相应的读取和写入 Jackson/CBOR 的速度高出 2.75 倍,要比相应的读取和写入 Jackson/JSON 的速度高出 5 倍。
灵活的对象
ION 另外一个与 JSON、MessagePack、和 CBOR 不同的是,ION 对象可以包含应用程序看到的适合的任意序列的 ION 字段。ION 对象,和 JSON 一样,可以包含名称、值的属性,但是它也可以包含其它混合序列的字段。举例来说,一个 ION 对象包含了一个 UTF-8 的字段序列,而且 ION 对象字段中是 XML 文本节点与 XML 元素混合。
ION 对象只能限于属性值,这样的结果就是能够非常紧凑的表示一个单一的对象,而且可以实现读写的速度匹配谷歌协议缓存(快写、慢读),但是却拥有了释放模式的优点还不需要对 Protobuf 的数据大小的原因说明。(Protobuf 承认其是一个对于文件等裸子节的编码不是最好的)
欲使用 ION 对象只能包含属性值需要你了解对象内部的字段序列,这或许对于公开的 API 不是一个好主意,但是对于紧耦合的、需要高性能的内部服务来说,此功能非常的有用。
任意层次的浏览
在早些时候提到过的基准测量是在 Java 对象中读取 ION 的速度,以及写 Java 对象到 ION 中,然而为了达到最大的速度,取代 ION 解析为 Java 对象,我们建议直接在二进制的表单中消化数据,记住,ION 就是被设计为如此用的模式的。
所有的 ION 字段都包含了以字节表示的字段值的长度。首先,以字节的方式知道 ION 字段可以让从二进制数据中提取值变得更加的快速和容易。你不需要去检查(解析)值的每个字节来看它在哪里结束,就如在诸如 XML 或 JSON 的文本格式所做的那样。第二,知道字段值的字节长度可以让不打算处理需要忽略的任何字段都更加的容易和快速。
任何的 ION 字段都可以包含带有同样包含其值的字节长度的 ION 字段(诸如对象、表、以及数组 ),这可以让忽略掉一整个对象树的“分支”更加的容易和快速,还毋需解析内部的字段。这就是我们所说的“任意层次的浏览”的含义:你可以快速而轻松的内外浏览类树的数据结构。
任意层次的浏览是 ION 所提供的明显比 MessagePack 和 CBOR 改进的一块内容,从编码来说仅仅是很细微的不同。在 MessagePack 中,元素可以包含嵌套的元素列表包含元素内的元素数量 - 不是字节的数量。所以若是在 MessagePack 或 CBOR 中要忽略掉嵌套元素中的元素的话,你需要遍历整个嵌套的元素直到找到所包含的元素。
我们曾经创建了一个简单的读取-使用的基准,来展现使用之前的解析 ION 数据为对象和直接在二进制表单中使用 ION 数据的不同速度。此基准读取一个拥有 10 个对象的表,每个对象的属性就是类型的长度,计算它们的和。
第一个版本的基准是在一个 Java 对象树(通过我们的 Java 反射 API)中读取 ION 数据,这是在计算长度属性的和之前。第二个版本的基准是直接从裸的 ION 数据中计算长度属性的和。
直接从裸的 ION 数据中计算长度属性的和要比通过基于反射 API 先解析到 Java 对象快 10 倍。大约比使用谷歌协议缓存(其亦会解析 Protobuf 消息为 java 对象)快 3 倍,大约比使用解析 JSON 为 java 对象和使用对象(使用 Jackson 的基于反射的 API)快 15 到 40 倍。
另外,基准使用了所有的 ION 数据树,如果计算器仅使用了部分的对象树的话,在解析二进制数据到对象和直接使用二进制数据之间的性能改进可能更加的令人膛目结舌。
ION 即文件格式
ION 虽被设计为用于网络消息的数据格式,但是也可以用作文件的格式。在 VStack.co ,我们不仅将 ION 用于数据文件还用于日志文件。对于我们来说使用单一的数据格式让我们和数据打交道更加的容易。网络消息可以写入磁盘以便于以后用于分析或回复,而且来自文件的数据可以很容易的被包含进网络消息中。
更多关于 ION 和其它的数据格式的对比内容
我们对 ION 在串行化长度和读写的性能方面做过一个基准的细节报告。
我们还做了 ION 和其它数据格式的对比,更多详情请移步: ION vs 其它数据格式。
IAP 消息结构
IAP 消息是编码过的一个单一的 ION 对象字段。两个节点通过 IAP 的交换 ION 对象字段来进行通信,ION 对象字段包含了嵌套的 ION 字段。嵌套的字段组成了消息头和消息体。
因为 IAP 消息均是 ION 对象字段,一个服务收到一 IAP 消息就会知道此消息的开始的 16 子节,以及第一个 4 字节到 5 字节,整体的消息到底有多长。这就让服务器很容易去为小型的消息分配合理的内存,激活服务接收大量的小型的消息,最好是利用好其内存。
每个 IAP 消息就是一个 ION 对象字段对于当一个完整的消息到达时更加容易跟踪。一旦接收到了由 ION 对象字段宣称的字节数量,就知道了消息的全部已经接收完毕。
我们目前所实现的 IAP 服务器在单核单线程的主机上每秒可以回声(ping-pong)200K(36 字节),其中客户端是 4 台和服务器一样的物理主机。服务器是常见的硬件盒子,4 核的 CPU,Hashwell 架构,内存是 DDR 3。客户端发送一条消息过来,然后服务端再回复一条。当客户端接收到了来自服务端的回复后,接着发送下一条,然后服务端再回复,如此反复。
若客户端将消息管道化了,然后服务端运行 4 核而非单核的话,我们可以在每秒钟达到 100 万条消息。
因为 ION 对象字段是紧凑的而且容易解析,所以 ION 还比较适应 CPU 和内存有限的小型设备。这一特点让 IAP 不仅适应典型的 Web 应用和后端服务,还适合物联网。
IAP 语义协议
尝试去定义一个单一的网络协议去适应当前的和未来的用例是困难的,但不是不可能。我们以实际行动去做了,IAP 被设计为由多个较小的协议组成,且可以组合起来使用。这些协议被分为一组核心的协议和一组语义协议。
核心协议规范了通用功能的实现,诸如接受消息、缓存等是如何工作的。此功能在网络协议中有着广泛的用途,提供了跨协议的功能。
语义协议交付了具体的用例实现,诸如文件交换、流、聊天室、VoIP 等等,IAP 将会定义一组标准的语义协议,但是更多是会是会用户提供更加灵活的可自定义的、自己的语义协议。
所有的核心和语义协议都将会使用相同的基本消息结构。很多的语义协议也将会使用相似的通信模式。这也就意味着应该实现一个单一的消息面向用来最多的服务器平台。如果不是全部,则是核心和语义协议。
核心和语义协议目前为止均为完成实现,但是重大的设计决策已经敲定。我们将会在这两协议均稳定后在发布。
IAP 传输协议
因为一个消息的交换模式或者是单一的协议是无法满足每一个用例的,那么一个传输协议也不能满足所有。因此 IAP 被设计为运行在 TCP 和 UDP 之上。最终看起来会是什么样的情形,现在还无从得知,但是我们正在努力的实现。
IAP 常见的问题
对于我们来说,此文并非是第一次和广大的开发者们讨论 IAP 的,下面的几个小节是一些个原来我们收到的反馈和回复。
一个二进制的协议是难以调试的
这是实话,一点都没有错。但是,首先,多数都数据库都使用专有的、二进制的协议来在数据库服务器和客户端 API 之间传输数据,为什么就没有听到任何的人们抱怨这些数据库?
第二,ION 是可以被转换为 XML ,且逆向也是可行的,还不回丢失任何的数据,这也就意味着你可以将 ION 消息转换为 XML,然后使用文本编辑器将其打开,甚至在修改过后再将之转换为 ION ,用于单元测试、调试之类的,至于 ION 和 XML 之间的互通目前暂时还没有实现,我们期望能在今年的第一个季度将此功能实现了。
当使用 ZIP 来压缩数据时紧凑与否已经无关紧要
一个比较普遍反对紧凑数据的是你可以只需在线路中使用 ZIP 压缩即可。然后,因为 CRIME 和 BREACH 攻击的原因,目前建议不要在加密的线路中(TLS/SSL)中使用数据压缩。从这个角度来讲,ION 的紧凑表编码做了很明显的差异。
即便是这样,如果所有的这些 JSON 字节都做 ZIP 压缩,它们最终还是需要解压缩并做解析的,这都是需要消耗 CPU 的。ZIP 压缩可能会降低数据的传输时间,但是它会增加解析的时间。即使是增加的量较小,但仍然是不必要的。
IAP 以及 ION 还没有达到可用于生产或成熟可用阶段
嗯,目前 IAP 和 ION 并没有大规模的使用,就像 HTTP 或 JSON 那样被实际证明是可行的。尽管如此,我们还是决定将内部的应用使用 IAP 来替代了 HTTP,所有 VStack.co 平台核心数据的出入都将会基于 IAP,如果 IAP 或 ION 有什么问题的话,我们肯定能第一时间发现。
一种实现并不能满足所有的需求
当然不能,这也是我们为什么将 IAP 设计为简单点核心消息架构可扩展的,用来适应不同的语义协议。
你看过或比较过其它的格式吗?
我们收到很多关于此问题相关的内容。我们将 ION 和 JSON、MessagePack、CBOR、以及 Protobuf 等均作了比较,更进一步,我们从 Cap’n Proto、Avro、Thrift 等身上学习了很多。编码本身并没有不同,所以速度需要去比较,而且更多的是依赖于实现而不是格式。
然而,我们为 ION 所做的一个不同于其它的如 Cap’n Proto 或 Protobuf 等的地方在于 ION 是自描述的。这也就意味着你直接从 ION 文件中得到具体意义而无需特定的模式。这对于能够让 ION 的消息路由到并不知道消息的模式的中间节点很有必要。
自我描述的特性让 ION 消息有一点点大还有解析起来还有一点点慢,但是它让 ION 更加的容易使用。你可以赋予数据和日志文件意义而无需知道日志消息写入到何种格式。你毋需担心丢失模式,或是将模式存放在文件的头部来防止丢失。你甚至可以将 ION 消息转换为文本格式以及再转换回来,根本就不需要知道模式。
更进一步,如果你需要进一步增加传输速度,你可以精简 ION 的元数据,这样你的消息的大小就非常接近需要模式(例如,Protobuf)的数据格式了。即便如此,你依然可以赋予 ION 消息中数据以意义,因为每个领域仍明确标记为二进制数据。
IAP 工具
我们就和 IAP 及 ION 打交道的开放的 API 叫做 IAP 工具。目前为止,我们仅实现了 Java 的版本,我们正在开发 D 和 C# 语言的 IAP API,如果事实证明这些是成功的话,我们会再考虑迁移到其它语言。
你可以从这里下载到针对Java 到IAP 工具。
代码目前还不能用于生产环境,但是已经足够可以让用户在自己的系统中能够和 ION 打交道并作前期的评估了。我们期望能够在今年的第一季度交付 ION 相关的代码。至于 IAP 的部分则需要稍长一些时间。
另外一个被认为是支持 ION 的工具包是: QBit (由 Rick Hightower 所牵头),QBit 是一个高速的微服务工具包,QBit 服务的内部通信通过消息来完成,如今,QBit 服务通常使用 JSON 来作为消息的格式,使用 ION 来替代 JSON 将会获得更小的消息、更快的读/写以及更加灵活的数据结构选择。
我们需要你的反馈
我们非常希望能够听到你对新的协议的看法。我们已经收到很多开发者们对于 HTTP 的抱怨;请告诉我们你为什么这么认为,以便于我们开发的 IAP 能够解决你的问题。
一个新的网络协议意味着需要写很多新的软件,但是它也能够开辟许多新的可能性-尤其是如果我们可以做出比 HTTP 更加通用的网络协议的话。
关于作者
Jakob Jenkov 是一名承包商、作家、亦是一名开发者,他是 Jenkov Aps( http://jenkov.com ) 的创始人兼 CEO,同时也是 VStack.co( http://vstack.co )的创始人兼 CTO,他的站点( http://tutorials.jenkov.com )包含了 750 多篇关于技术的文章。
Jakob 从 1997 年就开始了自己的 Java 程序编写生涯,他是在哥本哈根的 IT 大学里获得的信息技术学士学位的。