对 AMQP 协议“在幕后”所做的事情有一个很好的概念性理解在这里很有用。我会提供 AMQP 0.9.1 选择部署的文档和 API 使这特别令人困惑,所以这个问题本身就是许多人必须努力解决的问题。
TL;博士
连接是与AMQP 服务器协商的物理 TCP 套接字。正确实现的客户端将在每个应用程序中拥有其中一个,线程安全的,可在线程之间共享。
通道是连接上的单个应用程序会话。一个线程将有一个或多个这些会话。AMQP 架构 0.9.1 是这些不能在线程之间共享,并且应该在创建它的线程完成时关闭/销毁。当发生各种协议违规时,它们也会被服务器关闭。
消费者是一个虚拟构造,表示特定频道上存在“邮箱”。消费者的使用告诉代理将消息从特定队列推送到该通道端点。
连接事实
首先,正如其他人正确指出的那样,连接是代表与服务器的实际 TCP 连接的对象。连接是在 AMQP 的协议级别指定的,与代理的所有通信都通过一个或多个连接进行。
- 因为它是一个实际的 TCP 连接,所以它有一个 IP 地址和端口号。
- 协议参数是在每个客户端的基础上协商的,作为建立连接的一部分(一个称为握手的过程。
- 它被设计为长寿命;连接关闭是协议设计的一部分的情况很少。
- 从 OSI 的角度来看,它可能位于第 6 层附近
- 可以设置心跳来监视连接状态,因为 TCP 本身不包含任何内容来执行此操作。
- 最好有一个专用线程管理对底层 TCP 套接字的读取和写入。大多数(如果不是全部)RabbitMQ 客户端都会这样做。在这方面,它们通常是线程安全的。
- 相对而言,创建连接是“昂贵的”(由于握手),但实际上,这并不重要。大多数进程实际上只需要一个连接对象。但是,如果您发现需要比单个线程/套接字提供的吞吐量更高的吞吐量(在当前的计算技术下不太可能),您可以在池中维护连接。
渠道事实
Channel是为应用程序的每个部分打开的应用程序会话,用于与 RabbitMQ 代理进行通信。它通过单个连接运行,并代表与代理的会话。
- 由于它代表应用程序逻辑的逻辑部分,因此每个通道通常存在于自己的线程上。
- 通常,您的应用打开的所有通道都将共享一个连接(它们是在连接之上运行的轻量级会话)。连接是线程安全的,所以没关系。
- 大多数 AMQP 操作都是通过通道进行的。
- 从 OSI 层的角度来看,通道可能在第 7 层左右。
- 通道被设计为瞬态的;AMQP 设计的一部分是通道通常会关闭以响应错误(例如,在删除现有队列之前重新声明具有不同参数的队列)。
- 由于它们是瞬态的,因此您的应用程序不应汇集通道。
- 服务器使用整数来标识通道。当管理连接的线程接收到特定通道的数据包时,它使用此编号告诉代理该数据包属于哪个通道/会话。
- 通道通常不是线程安全的,因为在线程之间共享它们是没有意义的。如果您有另一个线程需要使用代理,则需要一个新通道。
消费者事实
消费者是 AMQP 协议定义的对象。它既不是通道也不是连接,而是您的特定应用程序用作某种“邮箱”来丢弃消息的东西。
- “创建消费者”意味着您告诉代理(通过连接使用通道)您希望通过该通道将消息推送给您。作为响应,代理将注册您在通道上有消费者并开始向您推送消息。
- 通过连接推送的每条消息都将引用一个通道号和一个消费者号。这样,连接管理线程(在这种情况下,在 Java API 中)知道如何处理消息;然后,通道处理线程也知道如何处理该消息。
- 消费者实现具有最广泛的变化,因为它实际上是特定于应用程序的。在我的实现中,我选择在每次消息通过消费者到达时分拆一个任务;因此,我有一个管理连接的线程,一个管理通道的线程(以及扩展的消费者),以及通过消费者传递的每条消息的一个或多个任务线程。
- 关闭连接会关闭连接上的所有通道。关闭通道会关闭通道上的所有消费者。也可以取消消费者(不关闭通道)。在各种情况下,做这三件事中的任何一件都是有意义的。
- 通常,AMQP 客户端中消费者的实现会为消费者分配一个专用通道,以避免与其他线程或代码(包括发布)的活动发生冲突。
就您所说的消费者线程池的含义而言,我怀疑 Java 客户端正在做的事情类似于我为客户端编写的程序(我的客户端基于 .Net 客户端,但经过大量修改)。