MQ初窥门径【面试必看的Kafka和RocketMQ存储区别】

 

MQ初窥门径

全称(message queue)消息队列,一个用于接收消息、存储消息并转发消息的中间件linux

应用场景

用于解决的场景,总之是能接收消息并转发消息缓存

  1. 用于异步处理,好比A服务作了什么事情,异步发送一个消息给其余B服务。
  2. 用于削峰,有些服务(秒杀),请求量很高,服务处理不过来,那么请求先放到消息队列里面,后面按照能力处理,至关于蓄水池。
  3. 应用解耦、消息通信等等

总之MQ是能够存放消息并转发消息的中间件,场景取决于拿这个能力去解决什么问题网络

MQ概念模型

MQ向别人承诺的场景是接收消息,存储,并能够转发消息异步

接收消息

接收消息,那么接收谁的消息,为了说明这个问题,那么mq须要引入一个概念,叫作生产者,也就是发送消息的服务,不然没有办法来区分是谁发的消息,生产者经过网络发送消息就能够,中间的细节咱们先不探讨。
那么还有一个问题就是消息发送给谁?性能

  1. 我在发送消息的时候,指明我要发送给谁,就像发送短信同样,你须要指明你要发送给谁?
    这种方案在使用中是有问题的,由于在如今业务不少场景中, 发送方其实根本不知道对方是谁,他只是将本身的状态发送出来,那么谁须要这个消息,谁就接收,第二个若是指明了接收方,那么之后增长一个接收方就要改一下配置或者代码,将发送消息的人跟接收消息的人绑定在一块儿了
    那么有没有方案,解耦的最好办法就是中间人,也叫中间层,我只发送给第三方,谁要消息,问第三方要,那么至关于我把发送的目标改成发送给第三方,这里的第三方就是mq,为了说明说明发送的地方,mq引入了topic的概念,发送方把消息发送到mq指定的一个通道中,之后谁想要这个消息,就跟mq说我想要这个通道的消息,也就是发送方发送的消息。

消费消息

消费消息,那么同理的一个问题,谁消费消息,为了说明那么mq须要引入一个概念,叫作消费者,也就是消费消息的服务,不然没有办法来区分是谁在接收消息,消费者经过网络接收消息就能够了,中间的细节咱们先不探讨。google

那么问题来了,消费者怎么说明消费谁的消息,上文已经说了,经过指明mq的topic,来决定我要哪一类消息。spa

至此咱们总结一下最后的模型3d

 

也就是最后生产者和消费者经过MQ的topic概念来实现解耦。code

存储

说到存储,其实效率才是最主要的,容量不是咱们关心的,可是说到存储,不仅是mq,全部须要高效率的存储其实最后利用的核心都是同样的。中间件

  1. 随机写转换成顺序写
  2. 集中刷盘
为何随机写要转换为顺序写?

第一 如今主流的硬盘是机械硬盘
第二 机械硬盘的机械结构一次读写时间 = 寻道时间 + 旋转延迟 + 读取数据时间
那么寻道时间比较长,若是是顺序写,只须要一次寻道时间,关于机械硬盘整个过程,读者可自行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中地址。

存储对比

  1. 消息体存储的变化
    那么咱们先来看看kafka,假设partition有1000个,一个partition是顺序写一个文件,整体上就是1000个文件的顺序写,是否是就变成了随机写,因此当partition增长到必定数目后,kafka性能就会降低。而rocketmq是把消息都写到一个CommitLog文件中,因此至关于一个文件的顺序写。

  2. 为何索引文件(ConsumeQueue)的增长对性能影响没有那么partition大?
    (kafka也有索引文件,在这里只是想说明索引文件的增长跟partition增长的区别)
    虽然rocketmq是把消息都写到一个CommitLog文件中,可是按照上面的实例会有1000个ConsumeQueue,也就是一千个文件,那么为何就没有把顺序写变成随机写,带来性能的降低呢?首先就要介绍linux的pagecache

     

    咱们日常调用write或者fwrite的时候,数据尚未写到磁盘上,只是写到一个内核的缓存(pagecache),只有当咱们主动调用flush的时候才会写到硬盘中。或者须要回写的pagecache占总内存必定比例的时候或者一个应该回写的page超过必定时间尚未写磁盘的时候,内核会将这些数据经过后台进程写到磁盘中(总结就是达到必定比例,或者多长时间尚未回写,会被内核自动回写)。

    而后咱们如今来看看为何大量索引文件的顺序写没有像partition同样致使性能明显降低。ConsumeQueue只存储了(CommitLog Offet + Size + Message Tag Hashcode),一共20个字节,那么当commitlog定时任务刷盘以后,应该回写的pagecache的比例就会降低不少,那么ConsumeQueue的部分能够不用刷盘,就至关于ConsumeQueue的内容会等待比较长的时间聚合批量写入,而kafka每一个partition都是存储的消息体,由于消息体都相对较大,基本在kb之上。
    当一个partition刷盘的时候,应该回写的pagecache的比例下降的并很少,不能阻止其余partition的刷盘,因此会大量存在多个partition同时刷盘的场景,变成随机写。可是rocketmq消息都会写入一个commitLog,也就是顺序写。

因此咱们总结下这个点:

一、consumerQueue消息格式大小固定(20字节),写入pagecache以后被触发刷盘频率相对较低。就是由于每次写入的消息小,形成他占用的pagecache少,主要占用方一旦被清理,那么他就能够不用清理了。

二、kafka中多partition会存在随机写的可能性,partition之间刷盘的冲撞率会高,可是rocketmq中commitLog都是顺序写。

 

  欢迎关注博主公众号,后面会持续更新mq系列知识点,一块儿讨论。

       

相关文章
相关标签/搜索