目录
一、什么是MQ
全称(message queue)消息队列,一个用于接收消息、存储消息并转发消息的中间件
多用于解决异步、削峰、解耦等场景,是能接收消息并转发消息
- 异步:比如A服务做了什么事情,异步发送一个消息给其他B服务。
- 削峰:有些服务(秒杀),请求量很高,服务处理不过来,那么请求先放到消息队列里面,后面按照能力处理,相当于蓄水池
- 应用解耦、消息通讯等等
总之MQ是可以存放消息并转发消息的中间件,场景取决于拿这个能力去解决什么问题
二、MQ概念模型
MQ向别人承诺的场景是接收消息,存储,并可以转发消息
接收消息
在发送消息的时候,指明我要发送给谁,就像发送短信一样,你需要指明你要发送给谁?
这种方案在使用中是有问题的,因为在现在业务很多场景中, 发送方其实根本不知道对方是谁,他只是将自己的状态发送出来,那么谁需要这个消息,谁就接收,第二个如果指明了接收方,那么以后增加一个接收方就要改一下配置或者代码,将发送消息的人跟接收消息的人绑定在一起了
那么有没有方案,解耦的最好办法就是中间人,也叫中间层,我只发送给第三方,谁要消息,问第三方要,那么相当于我把发送的目标改为发送给第三方,这里的第三方就是mq,为了说明说明发送的地方,mq引入了topic的概念,发送方把消息发送到mq指定的一个通道中,以后谁想要这个消息,就跟mq说我想要这个通道的消息,也就是发送方发送的消息。
消费消息
消费消息,那么同理的一个问题,谁消费消息,为了说明那么mq需要引入一个概念,叫做消费者,也就是消费消息的服务,否则没有办法来区分是谁在接收消息,消费者通过网络接收消息就可以了,中间的细节我们先不探讨。
那么问题来了,消费者怎么说明消费谁的消息,上文已经说了,通过指明mq的topic,来决定我要哪一类消息。
至此我们总结一下最后的模型
也就是最后生产者和消费者通过MQ的topic概念来实现解耦。
三、存储
说到存储,其实效率才是最主要的,容量不是我们关心的,但是说到存储,不只是mq,所有需要高效率的存储其实最后利用的核心都是一样的。
- 随机写转换成顺序写
- 集中刷盘
为什么随机写要转换为顺序写?
- 现在主流的硬盘是机械硬盘
- 机械硬盘的机械结构一次读写时间 = 寻道时间 + 旋转延迟 + 读取数据时间
那么寻道时间比较长,如果是顺序写,只需要一次寻道时间,关于机械硬盘整个过程,读者可自行google。
为什么集中刷盘?
因为每次刷盘都会进行系统调用,第二还是跟硬盘的本身属性有关,无论是机械硬盘还是ssd按照一定块刷盘会比小数据刷盘效率更好。
四、kafka
为什么先说kafka的存储,因为kafka是第一个高性能的消息中间件,其中rocketmq也是借鉴于它,所以我们先说
1、为什么引入消费组概念?
上一次模型图我们还没有消费组,那么引入消费组,是因为现在一个服务都有很多实例在运行,消费组是对这群一群机器的一个划分,他还是一个概念而已。
2、mq内部也发生了变化,一个topic后面又对应了很多partition,partition也是一个概念,他只不过是把一个topic分成了很多份,每一份叫一个partition,你高兴也可以叫他xxx,那么我们来说说为什么要分成很多份,一份不行吗?
因为现在一个服务有很多实例在运行,如果topic只有一份的话,那么所有的实例都会来消费消息,并且都是抢占我们一个topic,这不可避免引入了多实例竞争,以及他们之间怎么协调,一堆问题需要关注解决,现在我把topic分成了很多份,每一份只给一个实例,那么就不会引入各实例之间的竞争问题了,简化了mq的问题。
3、生产组的引入也是一样的,只不过是一组机器的一个概念,一个逻辑的划分,生产者发送消息原先是发往topic,那么现在topic分成了很多份,生产者发送消息,需要说明发往哪个partition或者随意分配都可以,只不过最终发送的消息,会到一个topic下的一份里面。无论使用哪种映射方式都可以。
那么模型出来了,我们说说存储的问题。
对于kafka,一个partition对应一个文件,每次消息来都是顺序写这个文件。并且是定时刷盘,而不是每次写都刷盘,所以kafka的写非常高效。
五、RocketMQ
rocketmq借鉴于kafka,所以存储借鉴了kafka,但是rocketmq不是仅仅把partition改成了ConsumeQueue,原先kafka,里面partition存储的是整个消息,但是现在ConsumeQueue里面是存储消息的存储地址,但是不存储消息了;
现在每个ConsumeQueue存储的是每个消息在commitlog这个文件的地址,但是消息存在于commitlog中
也就是所有的消息体都写在了一个文件里面,每个ConsumeQueue只是存储这个消息在commitlog中地址。
六、相同之处
- 两者底层原理有很多相似之处,RocketMQ借鉴了Kafka的设计。
- 两者均利用了操作系统Page Cache的机制,同时尽可能通过顺序io降低读写的随机性,将读写集中在很小的范围内,减少缺页中断,进而减少了对磁盘的访问,提高了性能。
七、不同之处
1、存储形式
- Kafka采用partition,每个topic的每个partition对应一个文件。顺序写入,定时刷盘。但一旦单个broker的partition过多,则顺序写将退化为随机写,Page Cache脏页过多,频繁触发缺页中断,性能大幅下降。
- RocketMQ采用CommitLog+ConsumeQueue,单个broker所有topic在CommitLog中顺序写,Page Cache只需保持最新的页面即可。同时每个topic下的每个queue都有一个对应的ConsumeQueue文件作为索引。ConsumeQueue占用Page Cache极少,刷盘影响较小。
2、存储可靠性
- RocketMQ支持异步刷盘,同步刷盘,同步Replication,异步Replication。
- Kafka使用异步刷盘,异步Replication。
3、顺序消息
Kafka和RocketMQ都仅支持单topic分区有序。RocketMQ官方虽宣称支持严格有序,但方式为使用单个分区。
4、延时消息
- RocketMQ支持固定延时等级的延时消息,等级可配置。
- kfaka不支持延时消息。
5、消息重复
- RocketMQ仅支持At Least Once。
- Kafka支持At Least Once、Exactly Once。
6、消息过滤
- RocketMQ执行过滤是在Broker端,支持tag过滤及自定义过滤逻辑。
- Kafka不支持Broker端的消息过滤,需要在消费端自定义实现。
7、消息失败重试
- RocketMQ支持定时重试,每次重试间隔逐渐增加。
- Kafka不支持重试。
8、DLQ(dead letter queue)
- RocketMQ通过DLQ来记录所有消费失败的消息。
- Kafka无DLQ。Spring等第三方工具有实现,方式为将失败消息写入一个专门的topic。
9、回溯消费
- RocketMQ支持按照时间回溯消费,实现原理与Kafka相同。
- Kafka需要先根据时间戳找到offset,然后从offset开始消费。
9、事务
- RocketMQ支持事务消息,采用二阶段提交+broker定时回查。但也只能保证生产者与broker的一致性,broker与消费者之间只能单向重试。即保证的是最终一致性。
- Kafka从0.11版本开始支持事务消息,除支持最终一致性外,还实现了消息Exactly Once语义(单个partition)。
10、服务发现
- RocketMQ自己实现了namesrv。
- Kafka使用ZooKeeper。
11、高可用
- RocketMQ在高可用设计上粒度只控制在Broker。其保证高可用是通过master-slave主从复制来解决的。
- Kafka控制高可用的粒度是放在分区上。每个topic的leader分区和replica分区都可以在所有broker上负载均衡的存储。
- Kafka的这种设计相比RocketMQ这种主从复制的设计有以下好处:
- Kafka中不需要设置从broker,所有的broker都可以收发消息。负载均衡也做的更好。
- Kafka的分区选举是自动做的,RocketMQ需要自己指定主从关系。
- Kafka分区的复制份数指定为N,则可以容忍N-1个节点的故障。发生故障只需要分区leader选举下即可,效率很高。