跳到主要内容

RabbitMQ 2.7.0 和 2.7.1 发布

·9 分钟阅读
Steve Powell

先前发布的 RabbitMQ 版本 (2.7.0) 带来了更好的插件管理方式、客户端一站式 URI 连接、Java 客户端中的线程安全消费者,以及许多性能改进和错误修复。最新版本 (2.7.1) 本质上是一个错误修复版本;尽管它也使 RabbitMQ 与 Erlang R15B 兼容,并增强了一些管理界面。之前的版本没有发布博文,所以我将这两个版本合并到这一篇中。 (这些是我个人的评论,不具有约束力;委托或遗漏的错误完全由我个人承担 -- Steve Powell。)

插件

在 2.7.0 之前,如果您想使用插件,则需要将 .ez 文件放在 plugins 目录中,并重新启动 broker。在此目录中找到的任何插件都会在启动时安装。 这意味着两件事:插件不是随服务器一起提供的(即使是我们支持的插件),并且安装或卸载插件涉及移动文件以及确保安装了其他插件依赖项。插件的管理不必要地混乱。

在 2.7.0 中,我们引入了命令 rabbitmq-plugins,用于启用或禁用 plugins 目录中的任何插件。只需发出命令 rabbitmq-plugins list 即可查看有哪些插件文件,并使用 rabbitmq-plugins enable <plugin-name> 在下次 broker 启动时使用其中一个插件。无需移动任何文件 -- 它们可以一直保留在插件目录中 -- 并且所有 rabbit 插件现在都随服务器一起提供,默认情况下禁用。 该命令还了解哪些插件依赖于其他插件,并自动启用依赖项。现在,使用和管理插件变得更加容易。有关更多信息,请参阅插件页面

在 2.7.1 中,修复了 consistent-hash-exchange 插件在处理多个交换器时错误路由消息的问题。

通过 URI 连接

所有 rabbit 客户端(.NET、Java 和 Erlang)客户端都接受 amqp URI 方案进行连接。这是一个方便的一站式商店,允许通过单个 URI 或字符串参数提供用户名和密码、主机名和端口以及虚拟主机。例如

amqp://guest:ghost@rabbit01.coderus.moc:5672/vhost01

有关详细信息,请参阅各个客户端 API。

Java 线程

在 2.7.0 版本中,Java 客户端的线程结构已得到显著重新设计。在此版本之前,Java 客户端的 Consumer 回调方法中可以执行的操作以及哪些应用程序线程可以调用 Channel 方法都存在限制。这些限制是由于 Java 客户端的底层线程结构将通道线程与回调共享造成的。通道和连接对象上的锁意味着直接从 Consumer 调用 Channel 方法会导致死锁(少数例外,例如确认)。 提供了 QueueingConsumer 辅助类,以将应用程序与其中一些问题隔离开来,但代价是引入了另一个队列(在 Java 客户端中)。

使用新的线程结构,对哪些应用程序线程可以调用通道操作的限制要少得多,因为所有 Consumer 回调都在与通道分离的线程上执行。 实际上,现在可以配置连接以管理专门用于回调的线程池,从而保持每个通道内这些线程的执行顺序。简单的客户端应用程序可以采用默认设置,该设置提供了一个小的回调线程池,而复杂的客户端可以提供自己的 ExecutorService 对象,这使他们可以创建和管理线程池的大小和行为。 现在不再需要 QueueingConsumer,因为人们希望作为 Consumer 回调结果而执行的所有操作都可以在 Consumer 方法中直接完成,而不会发生死锁。 有关更多信息,请参阅Java API 指南

2.7.1 修复了 2.7.0 中 Java 客户端调整中的一些烦恼:我们无意中隐藏了一些 API,现在已恢复。 还存在一些潜在的资源泄漏,我们现在已修复。

性能

在 2.7.0 和 2.7.1 中,服务器的性能都进行了一些小的改进。 这些改进的范围非常广泛,我在这里只能简单介绍其中一些。

  • 首先,通过使用较低级别的基本文件操作,基本文件 I/O 得到了改进。 这允许某些操作并行发生,而这些操作以前是通过 Erlang 进程串行化的。 这消除了某些瓶颈,并稍微加快了包括服务器关闭在内的几个区域。
  • 有趣的是,不受上述调整影响的密集 I/O 区域是所谓的“消息存储”。 不出所料,这是存储消息的地方(存储消息有多种原因,不仅是消息持久性)。 RabbitMQ 没有使用传统的数据库,而是管理自己的文件存储来存储消息。 (传统的数据库几乎具有完全错误的任务队列性能特征 -- 最近使用的项目很可能是最后访问的项目。) 消息存储是服务器中最复杂的部分之一,因为它需要非常快速地响应,而不会因其执行的相对较慢的 I/O 操作而限制系统的其余部分。 它的行为有点像分页系统缓存,因为当消息等待写入时,如果随后读取、重写甚至删除消息,则可能会从写入列表中“窃取”消息。 但是,“超车”规则与分页系统的规则完全不同,并且在此版本中,组织结构已更改为允许某些删除“取消”尚未发生的存储请求。 这减少了不必要的写入,因此提高了每个队列的整体吞吐量。 广泛的测试表明,即使在负载下,性能也得到了提高,同时保持了可靠性。
  • 对于具有大量消费者(尤其是低利用率的消费者)的连接,存在次优处理。 对于相对不活跃的消费者,似乎存在开销。 在 2.7.0 中,这一点得到了改进,这意味着拥有大量低使用率的消费者对整体性能的影响要小得多。
  • 删除具有大量绑定的队列和具有大量绑定的交换器所花费的时间比我们希望的要长。 这在 2.7.1 中得到了加速。

HiPE 选项

Erlang 为某些平台提供了一个高性能编译器 (HiPE),通过该编译器,Erlang 模块可以编译为本机代码。 但是,此编译并不总是产生更快的系统,并且并非所有 Erlang 环境和版本都支持它。 在 2.7.0 中,我们引入了一个配置选项来使用 HiPE,并且在服务器启动时会自动执行重新编译。 并非每个 rabbit 模块都会重新编译,仅重新编译我们已确定可能从此处理中受益的模块。 尽管此选项会将启动时间延迟数十秒,但它会在运行时产生显着的性能改进,这对于某些较大的 rabbit 安装可能至关重要。

默认情况下,此选项已禁用,因为它实际上可能会影响行为(并非我们已检测到),并且性能改进未在我们的用户使用的所有环境中进行测试。 但是,如果它对您有用,请尝试使用。 如果您的 Erlang 环境不支持 HiPE,则会显示一条简短的诊断消息,并且该选项将被忽略。

我们非常希望了解您对此功能的体验。

重新排队的消息

RabbitMQ 处理 FIFO 队列。 如果一切顺利,消息放入队列的顺序与它们被消费的顺序相同。 但是,当消费者失败时,它收到的一些消息可能尚未被确认,在这种情况下,这些消息将被重新排队,以便再次传递。 这样,消息可能会显得重新排序。 在此版本之前,无法保证重新排队的消息的顺序。

从 2.7.0 开始,来自单个消费者的重新排队的消息的相对顺序得以保留。 因此,如果另一个消费者稍后收到它们,它们将以它们最初出现的相同顺序被消费。 当然,如果同一队列上的两个或多个消费者失败,则无法保证不同消费者重新排队的消息将保留其相对顺序。 但在大多数情况下,顺序很重要,这种保证应该足够了。

高可用性问题

2.7.1 中包含了一些针对高可用性功能的修复(主要在 2.7.0 中引入)。 这些修复与一些内存泄漏有关;主队列的恢复;频繁重启导致 HA 队列失败;以及在某些情况下从属队列(到主队列)的升级失败。 此代码区域的总体质量很高,但故障场景的复杂性(此功能专门用于防止故障场景)使其成为漏洞潜伏的沃土。 2.7.1 中修复的几乎所有错误都是由于罕见或晦涩的恢复或重启事件组合引起的,我们有信心剩下的意外很少。 当然,高可用性并不意味着保证可用性,因此在某些情况下我们无法恢复。

其他小的改进和错误修复

  • 如果您长时间运行 broker,则可能会包装其中一个内部 GUID(全局唯一标识符)。 显然这不是故意的,而且在任何情况下都不会在实践中引起问题,对吗? 好吧,它确实引起了问题! (您知道吗 -- 有些人长时间运行 broker!)我们已在 2.7.1 中修复了它。
  • 管理插件界面现在显示有关队列长度的更多信息,铲子信息也显示得更好,此外,还修复了许多与统计信息和 HA 从属信息有关的小问题。
  • rabbitmqctl eval <expr> 是新的(在 2.7.1 中),用于在 broker 节点中评估任意 Erlang 表达式。
  • .net 客户端会话自动关闭有时会返回 AlreadyClosed 异常(它应该不会这样做)。
  • STOMP 适配器不支持 reply-to 队列(它们不可重用),并且如果 SEND 帧提供了一个消息 ID 标头,则可能会在 MESSAGE 帧上提供多个消息 ID 标头。 我们现在检查后一种情况并拒绝 SEND。
  • 已删除一些不再在 Erlang R15B 中的函数(并重写了代码),以便 RabbitMQ 现在应该可以在最新的 Erlang 版本下构建和运行。 如果不能,请告诉我们!

感谢您的收听

与往常一样,rabbit 团队欢迎您对您的体验(无论是好是坏)提供反馈。 我们鼓励您使用 rabbitmq-discuss 邮件列表。

© . All rights reserved.