在这里,对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客户端,但经过大量修改)。