MQ初窥门径
全称(message queue)消息队列,一个用于接收消息、存储消息并转发消息的中间件
用于解决的场景,总之是能接收消息并转发消息
总之MQ是可以存放消息并转发消息的中间件,场景取决于拿这个能力去解决什么问题
MQ向别人承诺的场景是接收消息,存储,并可以转发消息
接收消息,那么接收谁的消息,为了说明这个问题,那么mq需要引入一个概念,叫做生产者,也就是发送消息的服务,否则没有办法来区分是谁发的消息,生产者通过网络发送消息就可以,中间的细节我们先不探讨。 那么还有一个问题就是消息发送给谁?
消费消息,那么同理的一个问题,谁消费消息,为了说明那么mq需要引入一个概念,叫做消费者,也就是消费消息的服务,否则没有办法来区分是谁在接收消息,消费者通过网络接收消息就可以了,中间的细节我们先不探讨。
那么问题来了,消费者怎么说明消费谁的消息,上文已经说了,通过指明mq的topic,来决定我要哪一类消息。
至此我们总结一下最后的模型,也就是最后生产者和消费者通过MQ的topic概念来实现解耦。
说到存储,其实效率才是最主要的,容量不是我们关心的,但是说到存储,不只是mq,所有需要高效率的存储其实最后利用的核心都是一样的。
1.随机写转换成顺序写
2.集中刷盘
第一 现在主流的硬盘是机械硬盘 第二 机械硬盘的机械结构一次读写时间 = 寻道时间 + 旋转延迟 + 读取数据时间 那么寻道时间比较长,如果是顺序写,只需要一次寻道时间,关于机械硬盘整个过程,读者可自行google。
因为每次刷盘都会进行系统调用,第二还是跟硬盘的本身属性有关,无论是机械硬盘还是ssd按照一定块刷盘会比小数据刷盘效率更好
为什么先说kafka的存储,因为kafka是第一个高性能的消息中间件,其中rocketmq也是借鉴于它,所以我们先说。
先给出最终模型变化图。
那么模型出来了,我们说说存储的问题。 对于kafka,一个partition对应一个文件,每次消息来都是顺序写这个文件。并且是定时刷盘,而不是每次写都刷盘,所以kafka的写非常高效。
上文我们说了rocketmq借鉴于kafka,所以存储借鉴了kafka,但是rocketmq不是仅仅把partition改成了ConsumeQueue,在这里做了变化,原先kafka,里面partition存储的是整个消息,但是现在ConsumeQueue里面是存储消息的存储地址,但是不存储消息了。
现在每个ConsumeQueue存储的是每个消息在commitlog这个文件的地址,但是消息存在于commitlog中。 也就是所有的消息体都写在了一个文件里面,每个ConsumeQueue只是存储这个消息在commitlog中地址。
我们平常调用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,也就是顺序写。
所以我们总结下这个点: 1、consumerQueue消息格式大小固定(20字节),写入pagecache之后被触发刷盘频率相对较低。就是因为每次写入的消息小,造成他占用的pagecache少,主要占用方一旦被清理,那么他就可以不用清理了。 2、kafka中多partition会存在随机写的可能性,partition之间刷盘的冲撞率会高,但是rocketmq中commitLog都是顺序写。