Ruby AMQP 基准测试
我决定对我 AMQP 编码器/解码器 (benchmarks) 针对 AMQP gem 中的旧版本进行一些基准测试,看看它的性能是否更好。到目前为止,我只做了一些最基本的优化,例如将可重用值存储在常量中,没有什么特别的(目前)。
我做了两组基准测试:使用我的 RBench 分支(支持自定义格式化程序,如将结果写入 YAML 文件)进行 CPU 时间基准测试,以及使用 Object.count_objects
(Ruby 1.9) 进行内存基准测试。
我决定对我 AMQP 编码器/解码器 (benchmarks) 针对 AMQP gem 中的旧版本进行一些基准测试,看看它的性能是否更好。到目前为止,我只做了一些最基本的优化,例如将可重用值存储在常量中,没有什么特别的(目前)。
我做了两组基准测试:使用我的 RBench 分支(支持自定义格式化程序,如将结果写入 YAML 文件)进行 CPU 时间基准测试,以及使用 Object.count_objects
(Ruby 1.9) 进行内存基准测试。
在许多消息传递场景中,您绝不能丢失消息。 由于 AMQP 对消息持久性/处理几乎不提供保证,因此传统的做法是使用 事务,但这可能会慢得令人无法接受。 为了解决这个问题,我们以轻量级发布者确认的形式引入了 AMQP 的扩展。
RabbitMQ 2.3.1 引入了几个新的插件机制,让您可以更好地控制用户如何针对 Rabbit 进行身份验证,以及我们如何确定他们被授权做什么。这里有三个值得关注的问题
在 AMQP 的情况下,问题 1 由 SASL 回答 - SASL 是一种简单的可插拔身份验证机制协议,它嵌入在 AMQP(和各种其他协议)中。SASL 允许客户端和服务器协商和使用身份验证机制,而“外部”协议无需了解身份验证工作原理的任何细节。
SASL 提供了许多“机制”。自始至终,RabbitMQ 都支持 PLAIN 机制,该机制基本上包括以明文形式通过网络发送用户名和密码(当然,整个连接可能受 SSL 保护)。它还支持变体 AMQPLAIN 机制(在概念上与 PLAIN 相同,但如果您有 AMQP 编解码器,则更容易实现)。RabbitMQ 2.3.1 添加了一个插件系统,允许您添加或配置更多机制,并且我们编写了一个示例插件,该插件实现了 SASL EXTERNAL 机制。
有时,在我们的 邮件列表 和其他地方,会出现在 RabbitMQ 中使用不同后备存储的想法。后备存储是负责将消息写入磁盘的部分(消息可能出于多种原因写入磁盘),并且经常有人建议看看如果将 RabbitMQ 自己的后备存储替换为另一个存储系统会是什么样子。
这样的更改将允许当前不可能实现的功能,例如带外队列浏览或分布式存储,但像 RabbitMQ 这样的消息代理和通用数据库之间的数据存储和访问模式的性质存在根本差异。实际上,RabbitMQ 特意不在此类数据库中存储消息。
我很高兴地宣布 AMQP 0.7 已发布,正如我在 之前的博客文章 中承诺的那样。那么有哪些变化呢?
在过去一年中,AMQP gem 的开发实际上处于停滞状态,因为其原始作者 Aman Gupta (@tmm1) 很忙。许多错误仍未解决,代码变得陈旧过时,并且没有制作新功能或文档。
在这一点上,我开始与 RabbitMQ 的人谈论在这方面可能的合作。实际上,最初当我看到 Ezra Zygmuntowicz 为他的云团队寻找人员时,我联系了 VMware,但当我发现 VMware 最近在伦敦收购了 RabbitMQ 项目时,我产生了兴趣。我签署了合同,从 script/console
切换到 Wireshark 和 RabbitMQ Tracer,并且自 11 月以来,我一直愉快地 Hack AMQP 和 AMQ-Protocol gems。
我们一直在原型设计对新协议的支持,这符合我们的习惯。这个协议称为“AMQP 1.0 R0”,它是 AMQP 工作组(RabbitMQ 和后来的 VMware 都是其成员)的新版本。“R0”表示这是建议的第一个修订版。该规范不完整:有很多 TODO,并且在很大程度上它未经证实。这两个事实是促使进行此原型设计的部分原因。
原型代码镜像在 github 上:http://github.com/rabbitmq/rabbitmq-amqp1.0。它的构建方式与 我们所有的插件 相同。
AMQP 1.0 R0 规范与以前版本的 AMQP 规范不同,因为它没有定义代理模型;即,它没有定义交换队列和绑定,或它们的等效项。该协议实际上仅关于将消息从一个代理传输到另一个代理,然后就结果达成一致。这意味着它适用于附加到消息代理实现,以及其他用途——这个想法是人们可以调整现有模型以适应。
在我们的例子中,现有的模型是 AMQP 0-9-1,带有一些概括和扩展(例如,链式绑定)。因此,我们原型的目标是能够同时连接 1.0 客户端和 0-9-1 客户端来完成一些有用的工作。
好吧,好消息是,我们已经实现了这一点。实际上,该插件可以设置为替换 Rabbit 通常的网络侦听器,并且可以愉快地与 AMQP 0-8、0-9-1 和 1.0 客户端对话。我们确实在过程中做了一些发明,并且规范的某些部分我们明显没有实现。这些将在 README 中详细说明。
发明的一个重要部分是填写规范中沉默的语义。其中一些在 我们为 AMQP 工作组所做的此客户端-代理协议 工作中进行了详细说明。我们希望原型设计将有助于进一步完善这一点。
下周我将把我们的原型带到 AMQP 1.0 “Connectathon”,在那里它将针对核心协议的其他实现(并非所有实现都是开源的)进行测试。同样,这将有助于清除规范中互操作性的障碍。
RabbitMQ 需要更多更好的文档。(谁不需要呢?)特别是,我们需要更多更好的入门材料,向读者介绍各种基本概念,解释它们为什么重要,并激励他或她继续阅读并了解更多关于 RabbitMQ 的信息。这是该介绍的第一章。欢迎您的评论,第 2 章和第 3 章将很快推出。(您可能已经知道所有这些,但令人惊讶的是,很多人不知道。本介绍是为他们准备的。)
对于那些远离互联网的人来说,node.js 是一个基于 Google V8 的事件驱动 JavaScript 引擎。因为它本质上是一个大型高效的事件循环,所以它非常适合在状态很少的情况下来回洗牌数据的程序。而且编程很有趣,这显然是很多人的看法,因为围绕它涌现出了大量的库。
在这些库中,更令人印象深刻的是 Socket.IO。可以将 Socket.IO 与 node.js 的内置 Web 服务器结合使用来制作 websocket 服务器,并为浏览器提供套接字抽象,当没有 websocket 时,该抽象会降级为 XHR 技巧。(我很乐意相信 node.js 和 Socket.IO 是由一个仁慈而有远见的前驱种族为我们制作的;但当然,它们是由勤奋聪明的程序员制作的。谢谢你们!)
一旦在浏览器中有了套接字抽象,整个世界就打开了。具体来说,为了我们的目的,整个消息传递世界都打开了。由于 node.js 有一个 AMQP 客户端,我们可以轻松地将其与 RabbitMQ 连接起来;不仅可以桥接到其他协议和后端系统,还可以提供浏览器之间以及应用程序服务器之间的消息传递,等等。
继我们与 ZeroMQ 的 Martin Sustrik 合作的工作之后,我决定制作一个非常简单的协议,用于在浏览器套接字上使用,反映 ZeroMQ(以及 RMQ-0MQ) 中使用的消息传递模式——发布/订阅、请求/回复和 推送/拉取(或管道)。我编写了一个 node.js 库,该库使用 RabbitMQ 来使用其路由和缓冲实现消息模式;然后桥接是免费的,因为 RabbitMQ 有一堆协议适配器和各种语言的客户端。
消息传递模式的简要说明
发布/订阅 适用于已发布消息应传递给多个订阅者的情况。在一般情况下,可以使用各种类型的路由来过滤每个订阅者的消息。例如,这可以用于将来自后端系统的通知广播到用户的浏览器。
请求/回复 用于通过消息传递进行 RPC;请求在工作进程之间循环分配,回复路由回请求套接字。浏览器可以使用它来查询后端服务;甚至浏览器之间也可以相互查询。
管道 用于将进程链接在一起。消息以循环方式推送到工作进程,工作进程本身可能会推送到处理的另一个阶段。这可以用于协调用户组(或实际上是个人)之间的工作流程。
在适当地免除麻烦之后,这是 rabbit.js。
它只需要安装一个最基本的 RabbitMQ 和 node.js;以及 node-amqp 和 Socket.IO 库。这些东西的说明和位置都在 README 中。(请注意,您需要 我的 node-amqp 分支。)
它还包括一个微小的消息套接字服务器;也就是说,一个 node.js 服务器,它接受套接字连接并以长度前缀的消息进行通信。由于这一切都通过 RabbitMQ,您可以通过套接字与连接到 Socket.IO 的浏览器进行通信。您还可以从 node.js 本身运行的代码中使用进程内管道服务器。
总而言之,我惊讶于我可以用少量的代码和一些都击中了最佳点的技术完成多少工作——node.js 用于有趣的服务器编程,Socket.IO 用于神奇的浏览器套接字,RabbitMQ 用于无泪消息传递。
RabbitMQ 2.1.1 中新增了对交换机之间绑定的支持。这是 AMQP 规范的扩展,使用此功能(目前)将导致您的应用程序仅在 RabbitMQ 上运行,而不是在其他无数 AMQP 0-9-1 代理实现上运行。但是,此扩展大大提高了路由拓扑的表达性和灵活性,同时解决了一些可伸缩性问题。
正常绑定允许将交换机绑定到队列:发布到交换机的消息,如果满足交换机及其绑定的各种标准 ,将通过各种绑定并附加到每个绑定末尾的队列。这对于许多用例来说很好,但是灵活性很小:它始终只是一跳——消息被发布到一个交换机,具有一组绑定,因此可能有一组目的地。如果您需要更灵活的东西,那么您将不得不多次发布相同的消息。使用交换机到交换机的绑定,一次发布的消息可以流经任意数量的交换机,具有不同的类型,并且路由拓扑比以前可能实现的复杂得多。