队列模型与流式模型

队列与流式

以单个管道(也就是后续的分区的语义)为视角,可以分为队列与流式这两种语义,这两种语义最适合使用的场景有所不同。

  • 队列(Queue)语义:队列语义主要是采用无序或者共享的方式来消费消息。通过队列语义,用户可以创建多个消费者从单个管道中接收消息;当一条消息从队列发送出来后,多个消费者中的只有一个(任何一个都有可能)接收和消费这条消息。消息系统的具体实现决定了最终哪个消费者实际接收到消息。队列语义通常与无状态应用程序一起结合使用。无状态应用程序不关心排序,但它们确实需要能够确认(ack)或删除单条消息,以及尽可能地扩展消费并行性的能力。典型的基于队列语义的消息系统包括 RabbitMQ 和 RocketMQ。在队列消息系统中,一个队列可能有多个 Producer 和 Consumer。Producer 向队列发送消息,Consumer 从队列中接收消息。接收消息后,Consumer 开始处理消息,并在处理完每条消息后向队列消息系统发送 ack。由于多个 Consumer 共用一个队列,消息顺序并不重要,因此基于队列的系统很容易对 Consumer 进行扩展。消息队列系统适用于不需要按特定顺序执行任务的队列,例如,发送同一封邮件给多个收件人。RabbitMQ 和 Amazon SQS 都是基于队列的消息系统。

  • 流式(Stream)语义:相比之下,流式语义要求消息的消费严格排序或独占消息消费。对于一个管道,使用流式语义,始终只会有一个消费者使用和消费消息。消费者按照消息写入管道的确切顺序接收从管道发送的消息。流语义通常与有状态应用程序相关联。有状态的应用程序更加关注消息的顺序及其状态。消息的消费顺序决定了有状态应用程序的状态。消息的顺序将影响应用程序处理逻辑的正确性。在流消息系统中,Producer 追加数据到“仅追加”消息流中。在每个消息流中,必须按特定顺序处理消息,Consumer 在消息流中标记消息的位置。我们可以采取某种策略(如对用户 ID 进行哈希处理)对消息进行分区,使分区成为单独的数据流,增加并行度。由于每个流中的数据不可变,且只保存偏移 entry,因此处理时不会遗漏消息。流适用于重视消息顺序(如提取数据)的场景。Kafka 和 Amazon Kinesis 都使用流语义处理消息。

基于不同的消息语义,我们又可以从通讯模式的角度,理解消息队列可能会具备以下模式:

  • 点对点通讯:点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。
  • 多点广播:MQ 适用于不同类型的应用。其中重要的,也是正在发展中的是"多点广播"应用,即能够将消息发送到多个目标站点 (Destination List)。可以使用一条 MQ 指令将单一消息发送到多个目标站点,并确保为每一站点可靠地提供信息。MQ 不仅提供了多点广播的功能,而且还拥有智能消息分发功能,在将一条消息发送到同一系统上的多个用户时,MQ 将消息的一个复制版本和该系统上接收者的名单发送到目标 MQ 系统。目标 MQ 系统在本地复制这些消息,并将它们发送到名单上的队列,从而尽可能减少网络的传输量。
  • 发布/订阅(Publish/Subscribe)模式:发布/订阅功能使消息的分发可以突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息。发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发。
  • 集群(Cluster):为了简化点对点通讯模式中的系统配置,MQ 提供 Cluster(群集) 的解决方案。群集类似于一个域 (Domain),群集内部的队列管理器之间通讯时,不需要两两之间建立消息通道,而是采用群集 (Cluster) 通道与其它成员通讯,从而大大简化了系统配置。此外,群集中的队列管理器之间能够自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器可以接管它的工作,从而大大提高系统的高可靠性。
上一页