RocketMQ与Kafka IO区别

引言

简单说说RocketMQKafka的IO区别。如果文章写错了,请下面留言给我。

Kafka 存储原理

Kafka创建topic时会指定partition数量,在物理层面上有几个partition就有几个文件夹,类似这样:

topic与partition

topic名称-partition序号
topic名称为testpartition数量为3,在存储路径下(默认是/tmp/kafka****)就会有这3个文件夹:test-0test-1test-2

这里还有个consumer根据partition数量的负载均衡公式:
M = Math.ceil(PartitionSize/ConsumerCount)
Ci = [P(i * M),P((i + 1) * M -1)]
例如上面3个分区,那么2个消费者的情况下
M = Math.ceil(3/2) = 2
C0 = [P(0 * 2),P(1 * 1)] = [P(0),P(1)] 消费者0分配到分区0,1
C1 = [P(1 * 2),P(2 * 1)] = [P(2),P(2)] 消费者1分配到分区2

partition与segment

  • 每个partition下又有多个segment文件。
  • 每个segment对应3个文件index,log,timeindex(0.8版本之前没有timeindex),看名字就知道是将索引和日志本身区分开了。
  • segment数值最大为64位long大小,起始序号为上一个segment最后一条消息offset值。

举个栗子:比如上面的名为testtopic,我们进入test-0文件夹,里面会有这些文件:

  • 00000000000000000000.index (默认为10M大小的0x00填充文件,记录逻辑偏移对应的物理偏移)
  • 00000000000000000000.log
  • 00000000000000000000.timeindex(默认为(10M-4B)大小的0x00填充文件,记录当前时间戳对应的当前偏移-基础偏移)
  • leader-epoch-checkpoint (leader-epoch-checkpoint中保存了每一任leader开始写入消息时的offset会定时更新,follower被选为leader时会根据这个确定哪些消息可用,这个文件每个分区下只有一个)

由于是刚创建的topic,那么起始offset肯定就是0,这里说明一下如果这个segment最后一条消息偏移为3999,第二个segment结构开始从4000存储消息,那么文件就会是00000000000000003999.index这样开始。

如何利用索引确定消息位置

通过消息的逻辑offset二分查找即可确定index文件名,通过index文件再次查找距离最近的元数据逻辑offset去确定log文件物理offset,有了物理offset即可使用sendfile(java中对应的的是channel.transferTo)进行零拷贝发送了。(额外科普一条:In Linux kernels before 2.6.33, out_fd must refer to a socket. Since Linux 2.6.33 it can be any file。)

缺点:在分区数量过多topic数量过多的情况下,会打开很多个fd,无论是否使用内存映射或零拷贝技术,在分区数量过多的情况下它写入的性能都是直线下降的。这就是为什么Kafka大多会用来做日志,而不是高并发复杂业务场景下的流量削峰。

消息有序性

CloudStream的文章中我略过了partitionKeyExpressiond的配置,这里正好补上,继承PartitionKeyExtractorStrategy后自己可以实现,其意义在于自己选择分区,这就说明了一件事,在生产消息的时候我们可以确保某一类的消息到指定的partition中(在RocketMQ入门中讲过顺序消息),也就保证了消息的有序性,因为Kafka是顺序存储
需要注意的是,分布式情况轮询或多线程下,虽然接收到消息是有序的,但是你无法保证是否出现retry或处理这条消息时的时间多久,也许最后要做的事会变成无序状态,比如入库。

RocketMQ 存储原理

官方原图:
在这里插入图片描述
RocketMQ 设计 官方文档

消息存储架构图中主要有下面三个跟消息存储相关的文件构成。

  • CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。单个文件大小默认1G ,文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件。

  • ConsumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能,由于RocketMQ是基于主题topic的订阅模式,消息消费是针对主题进行的,如果要遍历commitlog文件中根据topic检索消息是非常低效的。Consumer即可根据ConsumeQueue来查找待消费的消息。其中,ConsumeQueue(逻辑消费队列)作为消费消息的索引,保存了指定Topic下的队列消息CommitLog中的起始物理偏移量offset消息大小size消息TagHashCode值consumequeue文件可以看成是基于topic的commitlog索引文件,故consumequeue文件夹的组织方式如下:topic/queue/file三层组织结构,具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。同样ConsumeQueue文件采取定长设计,每一个条目共20个字节,分别为8字节的commitlog物理偏移量4字节的消息长度8字节tag hashcode,单个文件由30W个条目组成,可以像数组一样随机访问每一个条目,每个ConsumeQueue文件大小约5.72M;

  • IndexFileIndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。Index文件的存储位置是:$HOME \store\index${fileName},文件名fileName是以创建时的时间戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引IndexFile的底层存储设计为在文件系统中实现HashMap结构,故rocketmq索引文件其底层实现为hash索引

大概就是说RocketMQ的物理IO句柄,就只有CommitLogConsumeQueue(理解为kafka中index文件的作用,当然消息长度等等也在这里面了),虽然也有分段设计,但是没有多个分区对应多个log的情况了,rocketmq将所有消息顺序写入一个文件中,虽然单机情况下吞吐量不如Kafka,但是在高并发topic数量增多的情况下,依然稳定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没事干写博客玩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值