本部分介绍排队通信背后的常规概念和核心概念。 后续部分详细介绍了下面所述的排队概念在 Windows Communication Foundation (WCF)中如何显示。
基本的队列概念
设计分布式应用程序时,为服务和客户端之间的通信选择正确的传输非常重要。 几个因素会影响要使用的传输类型。 服务、客户端和传输之间的一个重要因素(隔离)决定了使用排队传输或直接传输,例如 TCP 或 HTTP。 由于直接传输(如 TCP 和 HTTP)的性质,如果服务或客户端停止运行或网络失败,通信将完全停止。 服务、客户端和网络必须同时运行,才能使应用程序正常工作。 排队传输提供隔离,这意味着如果服务或客户端发生故障或它们之间的通信链接失败,客户端和服务可以继续正常运行。
即使通信方或网络发生故障,队列也能提供可靠的通信。 队列捕获和传送在通信方之间交换的消息。 队列通常由某种存储提供支持,该存储可以是易失性或持久性的。 队列代表服务存储来自客户端的消息,稍后将这些消息转发到服务。 间接寻址队列可确保隔离出现故障的任意一方,因此可将它作为高可用性系统和断开服务的首选通信机制。 间接性附带高延迟成本。 延迟 是客户端发送消息的时间与服务接收消息的时间之间的时间延迟。 这意味着一旦发送消息,就不知道何时可以处理该消息。 大多数的排队应用程序都需要处理高延迟。 下图显示了排队通信的概念模型。
排队通信概念模型
实际上,队列是分布式概念。 因此,它们可以是任意一方的本地队列,也可以是双方的远程队列。 通常,对于服务而言,队列是本地的。 在此配置中,客户端不能依赖于与远程队列的连接才能持续可用。 同样,队列的可用性必须与读取队列的服务的可用性无关。 队列管理器管理队列集合。 它负责接收其他队列管理器发送给它的队列的消息。 它还负责管理与远程队列的连接,并将消息传输到这些远程队列。 为了确保队列的可用性,尽管客户端或服务应用程序发生故障,队列管理器通常作为外部服务运行。
当客户端向队列发送消息时,它会将消息寻址到目标队列,即服务队列管理器管理的队列。 客户端上的队列管理器将消息发送到传输(或传出)队列。 传输队列是客户端队列管理器上的队列,用于存储消息以传输到目标队列。 然后,队列管理器找到拥有目标队列的队列管理器的路径,并将消息传输到该队列。 为了确保可靠的通信,队列管理器实现可靠的传输协议,以防止数据丢失。 目标队列管理器接受发送到其拥有的目标队列的消息,并存储这些消息。 服务发出从目标队列读取的请求,此时队列管理器会将消息传递到目标应用程序。 下图显示了四方之间的通信。
典型部署方案中的排队通信
因此,队列管理器提供所需的隔离,以便发送方和接收方可以独立地发生故障,而不会影响正常通信。 队列提供的额外间接优势也使多个应用程序实例能够从同一队列中读取数据,使节点之间的农业工作实现更高的吞吐量。 因此,队列用于实现较高的缩放和吞吐量需求的情况并不少见。
队列和事务
事务允许将一组操作组合在一起,以便于当一个操作失败时,所有操作都会失败。 如何使用交易的一个实例是,当一个人使用 ATM 将 1,000 美元从他们的储蓄帐户转入他们的支票帐户。 这需要以下操作:
从储蓄账户中提取 1,000 美元。
将 1,000 美元存入支票帐户。
如果第一次操作成功,且1,000美元已从储蓄账户中被提取,但第二次操作失败,那么这1,000美元就会被损失,因为它已经从储蓄账户中提取。 若要保持帐户在有效状态,如果一项操作失败,则两项操作都必须失败。
在事务消息传送中,消息可以在事务范围内发送到队列,并从队列中接收。 因此,如果在事务中发送消息而事务被回滚,则结果就如同消息从未被发送到队列中一样。 同样,如果在事务中接收消息并且该事务被回滚,则结果就像从未接收过消息一样。 消息仍会保留在队列中等待读取。
由于高延迟,发送消息时,无法知道到达目标队列所需的时间,也不知道服务处理消息需要多长时间。 因此,你不希望使用单个事务来发送消息、接收消息,然后处理消息。 这将创建不确定时间量内未提交的事务。 客户端和服务通过队列使用事务进行通信时,会涉及两个事务:一个在客户端上,另一个在服务上。 下面的插图显示了在典型排队通信中的事务边界。
排队通信,其中分别显示了捕获和传送事务
客户端处理事务并发送消息。 提交事务时,消息位于传输队列。 在服务上,事务从目标队列读取消息,处理消息,然后提交事务。 如果在处理过程中发生错误,则会回滚消息并将其放置在目标队列中。
使用队列进行异步通信
队列提供异步通信方式。 使用队列发送消息的应用程序无法等待接收方接收和处理消息,因为队列管理器引入的高延迟。 消息可以保留在队列中的时间比应用程序预期的要长得多。 为了避免这种情况,应用程序可以指定消息上的 Time-To-Live 值。 此值指定消息应保留在传输队列中的时长。 如果超过此时间值,并且消息仍未发送到目标队列,则可以将消息传输到死信队列。
发送方发送消息时,发送操作的返回意味着消息仅进入发送者的传输队列。 因此,如果在将消息发送到目标队列时出现故障,发送应用程序无法立即知道消息。 为了记录这些失败情况,失败消息将被传输到死信队列中。
必须单独处理任何错误,例如消息未能到达目标队列或者 Time-To-Live 过期。 因此通常情况下,排队应用程序会写入两组逻辑:
发送和接收消息的正常客户端和服务逻辑。
用于处理来自失败传输或传递的消息的补偿逻辑。
以下各节讨论这些概念。
Dead-Letter 队列编程
死信队列包含由于各种原因无法到达目标队列的消息。 原因可能从过期的消息到阻止将消息传输到目标队列的连接问题。
通常,应用程序可以从系统范围的死信队列中读取消息,确定出了什么问题,并采取适当的措施,例如更正错误并重新发送消息或记下消息。
病毒消息队列编程
将消息传送到目标队列后,服务可能会反复无法处理该消息。 例如,从事务下的队列读取消息并更新数据库的应用程序可能会发现数据库暂时断开连接。 在这种情况下,事务将回滚,会创建一个新的事务,并从队列中重新读取消息。 第二次尝试可能会成功或失败。 在某些情况下,根据错误的原因,消息可能反复无法传递到应用程序。 在这种情况下,消息被视为“毒药”。此类消息将移到病毒处理应用程序可以读取的病毒队列。