【问题标题】:RabbitMQ and relationship between channel and connectionRabbitMQ和channel和connection的关系
【发布时间】:2013-08-27 10:56:21
【问题描述】:

RabbitMQ Java client 有以下概念:

  • Connection - 与 RabbitMQ 服务器实例的连接
  • Channel - ???
  • 消费者线程池 - 从 RabbitMQ 服务器队列中消费消息的线程池
  • 队列 - 以 FIFO 顺序保存消息的结构

我正在尝试了解它们之间的关系,更重要的是关联

  1. 我仍然不太确定Channel 是什么,除了这是您发布和使用的结构,并且它是从打开的连接创建的。如果有人可以向我解释“频道”代表什么,它可能有助于澄清一些事情。
  2. Channel和Queue是什么关系?可以使用同一个 Channel 与多个 Queue 进行通信,还是必须是 1:1?
  3. Queue 和 Consumer Pool 是什么关系?多个消费者可以订阅同一个队列吗?同一个Consumer可以消费多个Queue吗?还是1:1的关系?

【问题讨论】:

  • 这个问题的答案导致我向 golang 客户端报告 this issue,而不是在这里提问。
  • 通道是一个逻辑概念,用于多路复用客户端和节点之间的单个物理 TCP 连接。通道号包含在 AMQP 帧的消息头中。

标签: java rabbitmq messaging amqp channel


【解决方案1】:

一个 TCP 连接可以有多个 Channels 之间存在一种关系。

Channel:是连接内的虚拟连接。从队列发布或使用消息时 - 这一切都通过通道完成而 连接:它是您的应用程序和 RabbitMQ 代理之间的 TCP 连接。

在多线程架构中,每个线程可能需要一个单独的连接。这可能会导致 TCP 连接的未充分利用,还会增加操作系统在网络高峰期建立所需数量的 TCP 连接的开销。系统的性能可能会大大降低。这是通道派上用场的地方,它在 TCP 连接中创建虚拟连接。它直接减少了操作系统的开销,还允许我们以更快、更可靠和同时的方式执行异步操作。

【讨论】:

    【解决方案2】:
    1. Connection 表示到消息代理的真实 TCP 连接,而Channel 是其中的虚拟连接(AMQP 连接)。这样,您可以在应用程序中使用任意数量的(虚拟)连接,而不会因 TCP 连接而使代理过载。

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

      Channel thread-safety in Java Client API Guide:

      通道实例可以安全地被多个线程使用。请求进入 一个 Channel 被序列化,只有一个线程能够运行一个 一次在频道上发出命令。即便如此,应用程序应该更喜欢 每个线程使用一个 Channel,而不是共享同一个 Channel 多个线程。

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

    3. 每个Consumer 都在从消费者线程池分配的自己的线程中运行。如果多个 Consumer 订阅了同一个 Queue,则 broker 使用循环方式在它们之间平均分配消息。见Tutorial two: "Work Queues"

      也可以将相同的Consumer 附加到多个队列。 您可以将消费者理解为回调。每次消息到达消费者绑定的队列时,都会调用这些函数。对于Java Client的情况,每个Consumer都有一个方法handleDelivery(...),代表回调方法。你通常做的是继承DefaultConsumer 并覆盖handleDelivery(...)。注意:如果将同一个 Consumer 实例附加到多个队列中,该方法将被不同的线程调用。因此,如有必要,请注意同步。

    【讨论】:

    • 只是从文档中添加:对消费者的回调是在与连接管理的线程分开的线程上调度的。这意味着消费者可以安全地调用 Connection 或 Channel 上的阻塞方法,例如 queueDeclare、txCommit、basicCancel 或 basicPublish。每个 Channel 都有自己的调度线程。对于每个通道一个消费者的最常见用例,这意味着消费者不会阻碍其他消费者。如果每个 Channel 有多个 Consumer,请注意长时间运行的 Consumer 可能会阻止向该 Channel 上的其他 Consumer 发送回调。
    • 如果你将同一个 Consumer 实例附加到来自同一个 Channel 的多个 Queue,这意味着回调是在同一个线程上调度的。在那种情况下,您不需要同步,对吗?
    • 我可以只使用一个连接并使用通道池而不是连接池吗?这会影响消息发布吞吐量吗?
    • 我认为这个对 Java Client API 的引用现在已经过时了,事实上今天的引用直接与这个答案中的引用相矛盾。今天的参考资料说“通道实例不能在线程之间共享”。
    • @EdwinDalorzo - 看起来最初编写文档的人并没有完全理解通道连接二分法。 AMQP 0.9.1 的基本架构确实将通道视为会话,因此共享会话的不同线程确实是无稽之谈。我猜这就是改变的原因。
    【解决方案3】:

    在这里,对 AMQP 协议“在后台”所做的工作有一个很好的概念性理解。我认为 AMQP 0.9.1 选择部署的文档和 API 使这特别令人困惑,所以这个问题本身就是许多人必须努力解决的问题。

    TL;DR

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

    channel 是连接上的单个应用程序会话。一个线程将有一个或多个这些会话。 AMQP 架构 0.9.1 是这些不能在线程之间共享,并且应该在创建它的线程完成时关闭/销毁。当发生各种协议违规时,它们也会被服务器关闭。

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

    连接事实

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

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

    频道概况

    Channel 是为应用程序的每个部分打开的应用程序会话,用于与 RabbitMQ 代理进行通信。它通过单个连接运行,并代表与代理的会话

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

    消费者概况

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

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

    就您所说的消费者线程池而言,我怀疑 Java 客户端所做的事情与我编写客户端执行的操作类似(我的客户端基于 .Net 客户端,但经过大量修改)。

    【讨论】:

    • “渠道不应该被汇集”,这就是我要找的
    • "由于它们是瞬态的,因此您的应用程序不应合并频道。" - 你能澄清一下你是如何得出这个结论的吗?如果“每个线程一个通道”实现使用过多资源,文档建议使用通道池,请参见此处:rabbitmq.com/channels.html#resource-usage
    • @ymas - 您所指的文档是推测性的,在我看来,指导性较差。我正在阅读源代码和协议规范。渠道不被汇集,期间。此外,每个线程一个通道是基于相同原理的引导。如果您发现您的开放通道太多以至于服务器资源受限,您需要重新评估您的架构(即切换到高可用性方案和/或减少并发)。
    • @theMayer 我认为您的立场仍然需要澄清。我正在开发一个拥有数十万客户和数千/秒发布消息速率的 Api。我正在考虑合并频道(保证一旦从池中挑选出其中一个频道,只有一个线程使用),我认为没有任何理由不这样做。
    • @MatteoSp,请随时提出新问题并标记我。我不想最终进入关于无关问题/答案的架构讨论。
    【解决方案4】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-23
      • 2023-03-04
      • 1970-01-01
      • 2016-05-12
      • 2022-07-19
      • 1970-01-01
      相关资源
      最近更新 更多