RocketMQ分布式存储
RocketMQ作为一款消息中间件,必然少不了数据的存储,跟Kafka一样,RocketMQ也是采取的分布式存储,这样就不至于一个Broker宕机,就产生数据丢失的问题,那么RocketMQ是如何实现分布式存储的呢?
如果大家了解Kafka的话,就知道在Kafka中存在一个数据分片partition,每一个topic可以划分为多个partition,而每一个partition都只存放一部分的数据,这些partition分布在不同的Broker上,所有的partition加起来才是完整的数据,且partition在不同的Broker上都会存在副本,保证了Kafka的高可用。说了这么多Kafka,那么RocketMQ到底是怎么实现数据的分布式存储呢?
在RocketMQ的官网,可以找到下面这一段的描述:
MessageQueue:将Topic分为一个或多个子Topic。
这不就是RocketMQ中的“partition”吗,Broker,Topic和MessageQueue的关系如下图:
MessageQueue是RocketMQ中的数据分片,RocketMQ通过MessageQueue将Topic拆分为多个数据分片,在每个Broker机器上都存储了一部分数据。既然知道了MessageQueue是分布式存储在Broker上,那么生产者是如何将产生的消息放入具体的MessageQueue中的呢?
生产者其实会从NameServer中获取到Topic的信息,RocketMQ在创建Topic的时候就需要制定MessageQueue,所以生产者进而就能获取到MessageQueue的信息,根据下图的策略就可以将数据分别写入到不同的MessageQueue中了,通过方法名就可以很直观的知道具体的选择方式,我就不再赘述了。
RocketMQ消息持久化
消息中间件除了写入消息和消费消息之外,也需要提供数据持久化的功能,一个典型的场景就是削峰,高峰期时大量的消息积压在MQ中,后期再慢慢消化。如果没有数据持久化的功能,第一内存有限,不一定能放得下那么多的消息数据,第二就是一旦MQ宕机,内存中的数据就会全部丢失。
所以Broker的数据存储实际上才是一个MQ最核心的环节。
那么RocketMQ是如何在持久化消息的同时,还保持如此大的吞吐量呢?
数据持久化的流程如上图所示。
- 首先Broker接收到Producer的消息,会将消息顺序写入到CommitLog中,CommitLog日志文件中就存储了消息的信息,CommitLog是多个文件,每个文件最大1GB,写满了之后,会创建新的CommitLog文件,这里面的一个优化点就是消息是顺序写入 CommitLog中的,顺序写入不需要再进行寻址,比文件的随机写性能要高很多。
- 消息写入的时候,并不是直接写入的磁盘中,而是先写入到了os cache中,写入内存的性能非常高,写入磁盘的话需要有IO的开销,会非常慢,而且这时写入的消息在消费时也可以直接从os cache中获取,同时加快消息消费的速度。
- 写入了os cache中还不算已经持久化,因为消息还在内存中,不在磁盘上,这时会有os线程通过异步的方式将os cache中的消息写入到磁盘中,异步刷盘完成之后才算真正的数据持久化了(异步刷盘性能较高,如果需要保证消息不会丢失,还是要采取同步刷盘的策略)。