各大消息队列优缺点
RabbitMQRocketMQkafka 基础概念
队列模型(点对点)发布 - 订阅模型(Publish-Subscribe Pattern)RabbitMQ 的消息模型RocketMQ 的消息模型Kafka 的消息模型 消费者如何消费消息?MQTT 协议 各大消息队列优缺点 RabbitMQ
缺点:
对消息堆积的支持并不好性能很差。每秒钟可以处理几万到十几万条消息Erlang 语言 优点:
轻量级、迅捷支持非常灵活的路由配置 RocketMQ
缺点:
与周边生态系统的集成和兼容程度要略逊一筹 优点:
响应时延很短,做了很多优化,大多数情况下可以做到毫秒级的响应,如果你的应用场景很在意响应时延,那应该选择使用 RocketMQ。性能还行。每秒钟大概能处理几十万条消息 kafka
缺点:
Kafka 这种异步批量的设计带来的问题是,它的同步收发消息的响应时延比较高,因为当客户端发送一条消息的时候,Kafka 并不会立即发送出去,而是要等一会儿攒一批再发送,在它的 Broker 中,很多地方都会使用这种“先攒一波再一起处理”的设计。当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高。所以,Kafka 不太适合在线业务场景。 优点:
性能优越。大量使用了批量和异步的思想,大约每秒钟可以处理几十万条消息Kafka 与周边生态系统的兼容性是最好的没有之一,尤其在大数据和流计算领域,几乎所有的相关开源软件系统都会优先支持 Kafka 基础概念 队列模型(点对点)
但是这种方式无法解决:将一份消息数据分发给多个消费者,要求每个消费者都能收到全量的消息,例如,对于一份订单数据,风控系统、分析系统、支付系统等都需要接收消息。这个时候,单个队列就满足不了需求,一个可行的解决方式是,为每个消费者创建一个单独的队列,让生产者发送多份。但是这样又会引来另一些问题:同样的一份消息数据被复制到多个队列中会浪费资源,更重要的是,生产者必须知道有多少个消费者。为每个消费者单独发送一份消息,这实际上违背了消息队列“解耦”这个设计初衷。
在发布 - 订阅模型中,消息的发送方称为发布者(Publisher),消息的接收方称为订阅者(Subscriber),服务端存放消息的容器称为主题(Topic)。发布者将消息发送到主题中,订阅者在接收消息之前需要先“订阅主题”。“订阅”在这里既是一个动作,同时还可以认为是主题在消费时的一个逻辑副本,每份订阅中,订阅者都可以接收到主题的所有消息。
少数依然坚持使用队列模型的产品之一。那它是怎么解决多个消费者的问题呢?在 RabbitMQ 中,Exchange 位于生产者和队列之间,生产者并不关心将消息发送给哪个队列,而是将消息发送给 Exchange,由 Exchange 上配置的策略来决定将消息投递到哪些队列中。
同一份消息如果需要被多个消费者来消费,需要配置 Exchange 将消息发送到多个队列,每个队列中都存放一份完整的消息数据,可以为一个消费者提供消费服务。这也可以变相地实现新发布 - 订阅模型中,“一份消息数据可以被多个订阅者来多次消费”这样的功能
所有的消息队列产品都使用一种非常朴素的发送以及 Ack 机制!,确保消息不会在传递过程中由于网络或服务器故障丢失。具体的做法也非常简单。在生产端,生产者先将消息发送给服务端,也就是 Broker,服务端在收到消息并将消息写入主题或者队列中后,会给生产者发送确认的响应。
如果生产者没有收到服务端的确认或者收到失败的响应,则会重新发送消息;在消费端,消费者在收到消息并完成自己的消费业务逻辑(比如,将数据保存到数据库中)后,也会给服务端发送消费成功的确认,服务端只有收到消费确认后,才认为一条消息被成功消费,否则它会给消费者重新发送这条消息,直到收到对应的消费成功确认。
这个确认机制很好地保证了消息传递过程中的可靠性,但是,引入这个机制在消费端带来了一个不小的问题。什么问题呢?为了确保消息的有序性,在某一条消息被成功消费之前,下一条消息是不能被消费的,否则就会出现消息空洞,违背了有序性这个原则。
也就是说,每个主题在任意时刻,至多只能有一个消费者实例在进行消费,那就没法通过水平扩展消费者的数量来提升消费端总体的消费性能。为了解决这个问题,RocketMQ 在主题下面增加了队列的概念。
每个主题包含多个队列,通过多个队列来实现多实例并行生产和消费。需要注意的是,RocketMQ 只在队列上保证消息的有序性,主题层面是无法保证消息的严格顺序的。
在 Topic 的消费过程中,由于消息需要被不同的组进行多次消费,所以消费完的消息并不会立即被删除,这就需要·RocketMQ 为每个消费组在每个队列上维护一个消费位置(Consumer Offset),这个位置之前的消息都被消费过,之后的消息都没有被消费过,每成功消费一条消息,消费位置就加一。这个消费位置是非常重要的概念,我们在使用消息队列的时候,丢消息的原因大多是由于消费位置处理不当导致的。
Kafka 的消息模型在 Kafka 中,队列这个概念的名称不一样,Kafka 中对应的名称是“分区(Partition)”,其余含义和功能与RocketMQ对应。
消费者如何消费消息?有两种模式,一种是 Kafka 推送给所有的消费者,另一种是由消费者拉取消息进行消费。
两者的比较如下:
推送模式: 基于推送模型的消息系统,由消息代理记录消费者的消费状态。 消息代理在将消息推送到消费者后 ,标记这条消息为已消费,但这种方式无法很好地保证消息的处理语义 。 比如,消 息代理把消息发送出去后,当消费进程挂掉或者由于网络原因没有收到这条消息时,就有可能造成消 息丢失(因为消息代理已经把这条消息标记为己消费了,但实际上这条消息并没有被实际处理)。 如果 要保证消息的处理语义,消息代理发送完消息后,要设置状态为“已发送”,只有收到消费者的确认请 求后才更新为“已消费”,这就需要在消息代理中记录所有消息的消费状态,这种做法也是不可取的。消息代理会在消息被消费之后立即删除消息。 如果有不同类型的消费者订阅同一个主题,消息代理可能露要冗余地存储同一条消息 ; 或者等所有消费者都消费完才删除,这就需要消息代理跟踪每个消费者的消费状态, 这种设计很大程度上限制了消息系统的整体吞吐量和处理延迟。
拉取模式: 由消费者自己记录消费状态,每个消费者乎相独立地顺序读取每个分区的 消息。 如图 1-4所示,有两个消费者(不同消费组)拉取同一个主题的消息,消费者A的消费进度是3, 消费者B的消费者进度是6。 消费者拉取的最大上限通过最高水位( watermark)控制,生产者最新写 入的消息如果还没有达到备份数量,对消费者是不可见的 。 这种由消费者控制偏移韭-的优点是 : 消费 者可以按照任意的顺序消费消息 。 比如,消费者可以重置到旧的偏移盏,重新处理之前已经消费过的消息 ;或者直接跳到最近的位置,从当前时刻开始消费 。
MQTT 协议如果要保持所有的消息完全有序,只能通过设置一个分区完成
在 MQTT 协议中,给出了三种传递消息时能够提供的服务质量标准,这三种服务质量从低到高依次是:
At most once: 至多一次。消息在传递时,最多会被送达一次。换一个说法就是,没什么消息可靠性保证,允许丢消息。一般都是一些对消息可靠性要求不太高的监控场景使用,比如每分钟上报一次机房温度数据,可以接受数据少量丢失。At least once: 至少一次。消息在传递时,至少会被送达一次。也就是说,不允许丢消息,但是允许有少量重复消息出现。Exactly once:恰好一次。消息在传递时,只会被送达一次,不允许丢失也不允许重复,这个是最高的等级。