RabbitMQ以及通道和连接之间的关系


176

RabbitMQ的Java客户端具有以下概念:

  • Connection -与RabbitMQ服务器实例的连接
  • Channel -???
  • 使用者线程池-消耗RabbitMQ服务器队列中的消息的线程池
  • 队列-一种按FIFO顺序保存消息的结构

我想了解的关系,更重要的是,该协会之间。

  1. Channel除了这是您从中发布和使用的结构,而且是从开放连接创建的事实之外,我仍然不太清楚a 是什么。如果有人可以向我解释“渠道”的含义,则可能有助于清除一些问题。
  2. Channel和Queue有什么关系?可以使用同一通道与多个队列进行通信,还是必须为1:1?
  3. 队列和使用者池之间有什么关系?可以将多个使用者订阅到同一队列吗?同一消费者可以使用多个队列吗?还是1:1的关系?

在此先感谢您的任何帮助!


这个问题的答案导致我向golang客户报告此问题,而不是在这里提问。
布鲁斯·亚当斯

通道是一个逻辑概念,用于多路复用客户端和节点之间的单个物理TCP连接。通道号包含在AMQP帧的消息头中。
ymas

Answers:


195
  1. A Connection代表到消息代理的真实TCP连接,而a Channel是其中的虚拟连接(AMQP连接)。这样,您可以在应用程序内部使用任意数量的(虚拟)连接,而不会使TCP连接使代理过载。

  2. 您可以Channel为所有内容使用一个。但是,如果您有多个线程,建议Channel对每个线程使用不同的线程。

    《 Java Client API指南》中的通道线程安全性

    通道实例可安全用于多个线程。进入Channel的请求被序列化,一次只能有一个线程在Channel上运行命令。即使这样,应用程序应该更喜欢每个线程使用一个Channel,而不是在多个线程之间共享同一Channel。

    Channel和之间没有直接关系Queue。A Channel用于将AMQP命令发送到代理。这可以是创建队列或类似的方法,但是这些概念并没有联系在一起。

  3. 每个Consumer线程都在从使用者线程池分配的自己的线程中运行。如果多个使用者订阅了同一队列,则代理将使用循环机制在它们之间平均分配消息。请参阅教程二:“工作队列”

    也可以将其附加Consumer到多个队列。您可以将使用者理解为回调。每当消息到达与消费者绑定的队列时,就会调用这些方法。对于Java客户端,每个使用者都有一个method handleDelivery(...),它表示回调方法。您通常要做的是subclass DefaultConsumer和override handleDelivery(...)。注意:如果将相同的Consumer实例附加到多个队列,则此方法将由不同的线程调用。因此,如有必要,请注意同步。


4
只是从文档中添加:消费者的回调是在与Connection管理的线程不同的线程上分派的。这意味着使用者可以安全地在Connection或Channel上调用阻塞方法,例如queueDeclare,txCommit,basicCancel或basicPublish。每个通道都有自己的调度线程。对于每个通道一个消费者的最常见用例,这意味着消费者不会拥挤其他消费者。如果每个通道有多个使用者,请注意长期运行的使用者可能会阻止向该通道上的其他使用者分配回调。
filip 2014年

1
如果将相同的Consumer实例从同一Channel附加到多个Queue,则意味着回调将在同一线程上分派。在这种情况下,您将不需要同步,对吗?
filip 2014年

我可以只使用一个连接并使用一个通道池而不是一个连接池吗?这会影响邮件发布的吞吐量吗?
6

4
我认为对Java客户端API的引用现在已过时,实际上,今天的引用与该答案中的引用直接矛盾。今天的参考资料说“通道实例不能在线程之间共享”。
Edwin Dalorzo

1
@EdwinDalorzo-似乎最初编写该文档的人都不完全了解通道连接二分法。AMQP 0.9.1的基本体系结构实际上将通道视为会话,因此共享会话的不同线程确实是胡说八道。我的猜测是这就是更改的原因。
theMayer

52

在这里,对AMQP协议在“幕后”的作用有一个很好的概念性理解是有用的。我要提供的是AMQP 0.9.1选择部署的文档和API使得这一点特别令人困惑,因此问题本身就是许多人必须努力解决的问题。

TL; DR

一个连接与AMQP服务器的物理协商TCP套接字。正确实现的客户端将在每个应用程序中具有线程安全,可在线程之间共享的每个应用程序之一。

信道是连接上的单个应用会话。一个线程将具有一个或多个这些会话。AMQP体系结构0.9.1禁止在线程之间共享这些属性,并且在创建它的线程完成后应将其关闭/销毁。当发生各种协议冲突时,它们也会被服务器关闭。

消费者是表示一个“信箱”的特定频道上的存在的虚拟构建体。使用者的使用告诉代理将消息从特定队列推送到该通道终结点。

连接事实

首先,正如其他人正确指出的那样,连接是代表与服务器的实际TCP连接的对象。连接是在AMQP中的协议级别指定的,并且与代理的所有通信都通过一个或多个连接进行。

  • 由于它是实际的TCP连接,因此具有IP地址和端口号。
  • 在建立连接的过程中,协议参数是在每个客户端的基础上协商的(此过程称为握手)
  • 它被设计为长寿的。在少数情况下,连接关闭是协议设计的一部分。
  • 从OSI角度来看,它可能位于第6层附近
  • 可以设置心跳来监视连接状态,因为TCP本身不包含任何内容。
  • 最好有一个专用的线程来管理对底层TCP套接字的读写。大多数(如果不是全部)RabbitMQ客户端都会这样做。在这方面,它们通常是线程安全的。
  • 相对而言,建立连接(由于握手)是“昂贵的”,但实际上,这并不重要。实际上,大多数进程只需要一个连接对象。但是,如果发现您需要的吞吐量比单个线程/套接字所能提供的吞吐量更高(与当前的计算技术不同),则可以在池中维护连接。

频道概况

一个通道是打开的,每件您的应用程序与RabbitMQ的代理进行通信的应用程序会话。它通过单个连接进行操作,并代表与代理的会话

  • 由于它代表应用程序逻辑的逻辑部分,因此每个通道通常存在于其自己的线程中。
  • 通常,您的应用打开的所有频道都将共享一个连接(它们是在连接之上运行的轻量级会话)。连接是线程安全的,因此可以。
  • 大多数AMQP操作都通过渠道进行。
  • 从OSI层的角度来看,通道可能围绕第7层
  • 通道被设计为瞬态的 ; AMQP设计的一部分是,通常会响应错误而关闭通道(例如,在删除现有队列之前重新声明具有不同参数的队列)。
  • 由于它们是瞬态的,因此您的应用程序不应合并频道。
  • 服务器使用整数来标识通道。当管理连接的线程接收到特定通道的数据包时,它使用此数字告诉代理程序该数据包属于哪个通道/会话。
  • 通道通常不是线程安全的,因为在线程之间共享它们是没有意义的。如果您还有另一个线程需要使用代理,则需要一个新通道。

消费者事实

使用者是AMQP协议定义的对象。它既不是通道也不是连接,而是您的特定应用程序用作某种“邮箱”来丢弃消息的东西。

  • “创建消费者”意味着您(告诉经纪人(使用通过连接渠道)希望通过该渠道向您推送消息。作为响应,经纪人将注册您在频道上有一个消费者,并开始向您发送消息。
  • 通过该连接推送的每个消息都将引用一个通道号和一个消费者号。这样,连接管理线程(在这种情况下,在Java API中)知道如何处理消息。然后,通道处理线程也知道如何处理消息。
  • 消费者实现的变化量最大,因为它实际上是特定于应用程序的。在我的实现中,我选择每次通过使用者到达一条消息时就剥离一个任务。因此,我有一个线程管理连接,一个线程管理通道(并扩展为消费者),以及一个或多个任务线程,用于通过消费者传递的每个消息。
  • 关闭连接将关闭连接上的所有通道。关闭频道会关闭该频道上的所有使用者。也可以取消使用者(不关闭通道)。在很多情况下,做三件事中的任何一件都是有意义的。
  • 通常,AMQP客户端中的使用者实现将为该使用者分配一个专用通道,以避免与其他线程或代码(包括发布)的活动发生冲突。

就使用者线程池的含义而言,我怀疑Java客户端正在执行与我的客户端编程相似的事情(我的客户端基于.Net客户端,但经过大量修改)。


1
“渠道不应该汇集”,这就是我在寻找的
ospider

“由于它们是瞬态的,因此不应由您的应用程序合并通道。” -请您能阐明您如何得出这个结论。如果“每个线程一个通道”实现使用的资源过多,则文档建议使用通道池,请参见此处:rabbitmq.com/channels.html#resource-usage
ymas

@ymas-您所指的文档只是推测性的,我认为指导性很差。我正在阅读源代码和协议规范。通道不被合并,周期。此外,基于该相同原理,每个线程一个通道是指导。如果发现开放通道太多,服务器受到资源限制,则需要重新评估体系结构(即切换到高可用性方案和/或减少并发性)。
Mayer

21

我发现这篇文章解释了AMQP模型的各个方面,其中通道是其中之一。我发现这对完善我的理解很有帮助

https://www.rabbitmq.com/tutorials/amqp-concepts.html

某些应用程序需要与AMQP代理建立多个连接。但是,不希望同时打开许多TCP连接,因为这样做会消耗系统资源,并使配置防火墙更加困难。AMQP 0-9-1连接与通道复用,这些通道可以被认为是“共享单个TCP连接的轻量级连接”。

对于使用多个线程/进程进行处理的应用程序,每个线程/进程打开一个新通道而不在它们之间共享通道是很常见的。

特定通道上的通信与另一通道上的通信是完全分开的,因此,每个AMQP方法还带有一个通道号,客户端可使用该通道号确定该方法用于哪个通道(例如,因此需要调用哪个事件处理程序) 。


4

就像一个TCP连接之间可以有多个Channel之间存在关系。

通道:它是连接内部的虚拟连接。当发布或使用队列中的消息时,都是通过通道完成的。而Connection:这是应用程序和RabbitMQ代理之间的TCP连接。

在多线程体系结构中,每个线程可能需要一个单独的连接。这可能导致TCP连接利用不足,也增加了操作系统的开销,无法在网络的高峰时间内建立所需数量的TCP连接。系统的性能可能会大大降低。这是通道派上用场的地方,它在TCP连接内创建虚拟连接。它直接减少了OS的开销,还使我们能够以更快,更可靠和同时的方式执行异步操作。 在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.