文章目录

一、RocketMQ

为什么有了kafka,阿里还要自建mq?

随着业务吞吐量的增长,在使用越来越多的队列和虚拟主题的情况下,ActiveMQ IO模块遇到了瓶颈。此时,业界出现了现在被很多大数据领域所推崇的Kafka消息引擎,但是Kafka不能满足阿里巴巴的要求,特别是在低延迟和高可靠性方面。相关的说明可以在官方文档中查看。

Kafka是一个分布式流媒体平台,诞生于日志聚合案例。它不需要太高的并发性。在阿里巴巴的一些大型案例中,我们发现原来的模型已经不能满足我们的实际需求。

因此,我们开发了一个名为 RocketMQ 的消息中间件,它可以处理广泛的用例,从传统的发布/订阅场景到要求苛刻的大容量实时事务系统,不能容忍任何消息丢失。现在,在阿里巴巴,RocketMQ 集群每天处理超过 5000 亿个事件,为超过 3000 个核心应用提供服务。

RockeMQ连接池

1.1 RocketMQ消息怎么保证可靠性以及高可用性

阿里云MQ消息重试机制验证

经测试,消费者短时间内返回消费失败结果的情况下,得出如下结论:

  1. 消费者未能正常消费时,MQ将重新投递消息;
  2. 消费者重试时间间隔与阿里云官方文档基本一致;
  3. 消费者重试超过16次之后不再进行重试,与阿里云官方文档一致;

https://help.aliyun.com/document_detail/43490.html?spm=a2c6h.12873639.0.0.55c82ac489YHGL

https://developer.aliyun.com/article/530024

https://www.bilibili.com/video/BV1TE411g74u?p=11

https://www.bilibili.com/video/BV1Ap4y1D7tU?p=28

高吞吐

http://www.ijiandao.com/2b/baijia/97775.html
吞吐方面,在小包非批量以及大量分区的场景下(现实应用更广泛的场景),RocketMQ 更能充分利用磁盘的 IO 能力达到更高的 TPS(领先 Kafka 一倍左右)。在大包和批量的场景下,RocketMQ 和 Kafka 目前已经相差无几,此时的瓶颈已经转移到磁盘的吞吐能力上。

1.2 RocketMQ消费协议与模式(push pull)

1.tcp

tcp在内网

在这里插入图片描述

2.http

HTTP协议公网接入点访问消息队列

在这里插入图片描述

在这里插入图片描述

rocketmq分为push与pull

push模式:推送模式,即服务端有数据之后立马推送消息给客户端,需要客户端和服务器建立长连接,实时性很高,对客户端来说也简单,接收处理消息即可;缺点就是服务端不知道客户端处理消息的能力,可能会导致数据积压,同时也增加了服务端的工作量,影响服务端的性能;

pull模式:拉取模式,即客户端主动去服务端拉取数据,主动权在客户端,拉取数据,然后处理数据,再拉取数据,一直循环下去,具体拉取数据的时间间隔不好设定,太短可能会导致大量的连接拉取不到数据,太长导致数据接收不及时;

RocketMQ使用了长轮询的方式,兼顾了push和pull两种模式的优点,下面首先对长轮询做简单介绍,进而分析RocketMQ内置的长轮询模式。

1.3 RocketMQ的消费模式

集群消费

当 consumer 使用集群消费时,每条消息只会被 consumer 集群内的任意一个 consumer 实例消费一次。举个例子,当一个 consumer 集群内有 3 个consumer 实例(假设为consumer 1、consumer 2、consumer 3)时,一条消息投递过来,只会被consumer 1、consumer 2、consumer 3中的一个消费。

同时记住一点,使用集群消费的时候,consumer 的消费进度是存储在 broker 上,consumer 自身是不存储消费进度的。消息进度存储在 broker 上的好处在于,当你 consumer 集群是扩大或者缩小时,由于消费进度统一在broker上,消息重复的概率会被大大降低了

注意:在集群消费模式下,并不能保证每一次消息失败重投都投递到同一个 consumer 实例。

广播消费

与集群消费不同的是,consumer 的消费进度是存储在各个 consumer 实例上,这就容易造成消息重复。还有很重要的一点,对于广播消费来说,是不会进行消费失败重投的,所以在 consumer 端消费逻辑处理时,需要额外关注消费失败的情况。

虽然广播消费能保证集群内每个 consumer 实例都能消费消息,但是消费进度的维护、不具备消息重投的机制大大影响了实际的使用。因此,在实际使用中,更推荐使用集群消费,因为集群消费不仅拥有消费进度存储的可靠性,还具有消息重投的机制。而且,我们通过集群消费也可以达到广播消费的效果。

使用集群消费模拟广播消费

如果业务上确实需要使用广播消费,那么我们可以通过创建多个 consumer 实例,每个 consumer 实例属于不同的 consumer group,但是它们都订阅同一个 topic。

举个例子,我们创建 3 个 consumer 实例,consumer 1(属于consumer group 1)、consumer 2(属于 consumer group 2)、consumer 3(属于consumer group 3),它们都订阅了 topic A ,那么当 producer 发送一条消息到 topic A 上时,由于 3 个consumer 属于不同的 consumer group,所以 3 个consumer都能收到消息,也就达到了广播消费的效果了。

除此之外,每个 consumer 实例的消费逻辑可以一样也可以不一样,每个consumer group还可以根据需要增加 consumer 实例,比起广播消费来说更加灵活。

rocketmq 架构组成

他主要有四大核心组成部分:NameServer、Broker、Producer以及Consumer四部分。

在这里插入图片描述

架构设计 保证高可用

NameServer VS zk

  • 我们可以对比下kafka和rocketMq在协调节点选择上的差异,kafka通过zookeeper来进行协调,而rocketMq通过自身的namesrv进行协调。

  • kafka在具备选举功能(CP),在Kafka里面,Master/Slave的选举,有2步:第1步,先通过ZK在所有机器中,选举出一个KafkaController;第2步,再由这个Controller,决定每个partition的Master是谁,Slave是谁。因为有了选举功能,所以kafka某个partition的master挂了,该partition对应的某个slave会升级为主对外提供服务。

  • rocketMQ不具备选举(AP),Master/Slave的角色也是固定的。当一个Master挂了之后,你可以写到其他Master上,但不能让一个Slave切换成Master。那么rocketMq是如何实现高可用的呢,其实很简单,rocketMq的所有broker节点的角色都是一样,上面分配的topic和对应的queue的数量也是一样的,Mq只能保证当一个broker挂了,把原本写到这个broker的请求迁移到其他broker上面,而并不是这个broker对应的slave升级为主。

  • rocketMq在协调节点的设计上显得更加轻量,用了另外一种方式解决高可用的问题,思路也是可以借鉴的。

  • rocketmq是通过多个master实现写入容灾,通过主从实现读取容灾(这一组Broker的Master挂了,但是这组中的Slave可以继续提供读的服务,直至把未消费完的消息全部读完;这一组的Master挂了,写的服务会找另一组的Master继续写)

RocketMQ由几部分组成以及每个组件的作用。

在这里插入图片描述

RocketMQ架构上主要分为四部分,如上图所示:

Producer

消息发布的角色,支持分布式集群方式部署。Producer通过MQ的负载均衡模块选择相应的Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。

Consumer

消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制,可以满足大多数用户的需求。

NameServer

NameServer是一个非常简单的Topic路由注册中心,其角色类似Kafka中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能:

  • Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;
  • 路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。
  • NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。(保持长链接)

路由信息是包括了:BokerServer,Topic和ConsumeQueueID等信息。

当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以动态感知Broker的路由的信息。

BrokerServer

Broker主要负责消息的存储、投递和查询以及服务高可用保证,为了实现这些功能,Broker包含了以下几个重要子模块。

RocketMQ消息种类以及怎么保证消息有序。

1.4 消息类型

延时消息

说明:rocketmq实现的延时队列只支持特定的延时时间段,1s,5s,10s,…2h,不能支持任意时间段的延时。

具体实现:
rocketmq发送延时消息时先把消息按照延迟时间段发送到指定的队列中(rocketmq把每种延迟时间段的消息都存放到同一个队列中)然后通过一个Timer定时器进行轮训这些队列,查看消息是否到期,如果到期就把这个消息发送到指定topic的队列中,这样的好处是同一队列中的消息延时时间是一致的,还有一个好处是这个队列中的消息时按照消息到期时间进行递增排序的,说的简单直白就是队列中消息越靠前的到期时间越早
在这里插入图片描述

事务消息

基于MQ的事务仅仅保证的是消息的可靠传递(确保生产者的消息一定可以被消费者接受到),并不保证消费者一定可以处理该消息,如果需要回滚,需要用TCC,2PC

为什么不能先创建订单 在发送消息消息失败 或者发送状态是不是SEND_OK 就回滚事务?(看似闭环,其实是有问题)

mq底层依赖网络

  • broker保存消息成功,但是响应producer成败,因网络或其他原因。这个时候,producer端其实没有执行事务。

  • 本地提交事务,发送消息超时 (网络抖动,发送超时,但是其实已经发送成功),本地事务回滚,消费者消费消息,造成数据不一致

事务消息是如何保证消息一定能被发送?
// 客户端需要有一个线程或者进程来消费没有确认的事务消息
	// 示例这里启动一个Goroutines来检查没有确认的事务消息
	go ConsumeHalfMsg(&mqTransProducer)

RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。

细心的你可能又发现问题了,如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认,Bob的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?

在这里插入图片描述

消费事务消息

这样基本上可以解决消费端超时问题,但是如果消费失败怎么办?阿里提供给我们的解决方法是:人工解决。在设计实现消息系统时,我们需要衡量是否值得花这么大的代价来解决这样一个出现概率非常小的问题,这也是大家在解决疑难问题时需要多多思考的地方。

1.5 生产者、消费者的负载均衡

生产者以轮询的方式向所有写队列发送消息,这些队列可能会分布在多个broker实例上。

生产者的负载均衡

Producer发送消息时,默认会轮询目标Topic下的所有MessageQueue,并采用递增取模的方式往不同的MessageQueue上发送消息,以达到让消息平均落在不同的queue上的目的。而由于MessageQueue是分布在不同的Broker上的,所以消息也会发送到不同的broker上。

在这里插入图片描述
同时生产者在发送消息时,可以指定一个MessageQueueSelector。通过这个对象来将消息发送到自己指定的MessageQueue上。这样可以保证消息局部有序。

消费者的负载均衡

Consumer也是以MessageQueue为单位来进行负载均衡。分为集群模式和广播模式。

一个 group 中的多个消费者,可以以负载均衡的方式来接收消息。

读取队列被均匀分配给这些消费者,它们从指定的队列来接收消息。队列的分配可以采用不同的策略

平均分配 默认策略
在这里插入图片描述

环形分配 在消费者的代码中需要设置分配策略
一致性哈希算法 在消费者的代码中需要设置分配策略

缺点:

  1. 顺序写,随即读

克服缺点:
由于Consume Queue存储数据量极少, 而且是顺序读, 在PAGECACHE预读作用下, Consume Queue的读性能几乎与内存一致, 即使堆积情况下. 所以可认为Consume Queue完全不会阻碍读性能

RocketMQ可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区

RocketMQ存储的特点:

1.Broker单个实例下所有的队列共用一个日志数据文件(即为CommitLog)来存储
2.consumerQueue 是个消费的逻辑队列,保存了数据在commit log中的offset
3. 消费读取数据,需要先读取consumerQueue,再读取commit log,消息主体都是通过CommitLog来进行读写.

读写分离

读消息 冷数据、热数据

1.6 消息存储与检索

RocketMQ的消息存储主要分为:CommitLog、ConsumeQueue和IndexFile,它们的关系如下图:

1.6.1 消息储存

在这里插入图片描述

1.CommitLog
  • 存储Producer端写入的消息元数据及主体内容,内容不定长,不区分topic
    每个文件大小固定,默认为1G,文章名固定为20字节,为起始的偏移量,如:00000000000000000000为第一个文件,00000000001073741824为第二个文件
  • 消息主要是顺序写入文件,当文件满了,写入下一个文件

RocketMQ采用的是混合型的存储结构。

Broker单个实例下所有的队列共用一个日志数据文件(CommitLog)来存储;即多个Topic的消息实体内容都存储于一个CommitLog中。

针对Producer和Consumer采用了数据和索引部分相分离的存储结构:

  • Producer发送消息至Broker端后,Broker端使用同步或者异步的方式对消息刷盘持久化,保存至CommitLog中。
  • Broker端启动一个后台服务线程—ReputMessageService不停地分发请求并异步构建ConsumeQueue(逻辑消费队列)和IndexFile(索引文件)数据。
2.ConsumeQueue
  • 主要目的:提高消息消费的性能,避免低效地遍历commitlog文件来检索topic消息
    本质是commitlog基于topic的索引文件,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息大小size和消息Tag的HashCode值。
  • consumequeue文件采取定长设计,每一个条目共20个字节,单个文件由30W个条目组成(约5.72M),目的是为了可以像数组一样随机访问每一个条目

RocketMQ的ConsumeQueue中不存储具体的消息,具体的消息由CommitLog存储,ConsumeQueue中只存储路由到该queue中的消息在CommitLog中的offset,消息的大小以及消息所属的tag的hash(tagCode),一共只占20个字节,整个数据包如下:

在这里插入图片描述

要想知道RocketMQ如何存储消息,我们先看看CommitLog。在RocketMQ中,所有topic的消息都存储在一个称为CommitLog的文件中,该文件默认最大为1GB,超过1GB后会轮到下一个CommitLog文件。通过CommitLog,RocketMQ将所有消息存储在一起,以顺序IO的方式写入磁盘,充分利用了磁盘顺序写减少了IO争用提高数据存储的性能,

消费者在读取消息时,先读取ConsumeQueue,再通过ConsumeQueue中的位置信息读取CommitLog,得到原始的消息。

在这里插入图片描述

3.IndexFile

IndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。

Index文件的存储位置是:$HOME\store\index${fileName},文件名fileName是以创建时的时间戳命名的;
固定的单个IndexFile文件最大约为400M,一个IndexFile可以保存 2000W个索引;
IndexFile的底层存储设计为在文件系统中实现HashMap结构,所以rocketmq的索引文件其底层实现为hash索引。

1.6.2 消息索引机制

通过messageID 提取消息

messageID = broker id +offset, 很容易找到commitLog文件读取消息

通过ConsumeQueue实现tag查询

在这里插入图片描述

消息刷盘

  • 同步刷盘:只有在消息真正持久化至磁盘后RocketMQ的Broker端才会真正返回给Producer端一个成功的ACK响应。可靠性高,性能低。
  • 异步刷盘:充分利用OS的PageCache的优势,写入PageCache即可将成功的ACK返回给Producer端。消息刷盘采用后台异步线程提交的方式进行。性能高和吞吐量大,可靠性不能100%保证。

RocketMQ异构存储的优劣

总的来说,RocketMQ所有消息数据都存储在commit log文件中。所有topic的消息的写入都是完全顺序的。为了加速消息的消费,利用ConsumeQueue存储实际的用户消费位置信息,而信息也以顺序方式刷新到磁盘,且粒度小。

  • 优点:

每个消耗队列都是轻量级的,并且包含数量有限的元数据。
对磁盘的访问是完全顺序的,避免了磁盘锁争用
可以低成本地通过增加队列来提高并发量,并且创建大量队列进行生产和消费时不会导致高磁盘IO等待。
ConsumerQueue可以直接load到内存中随机访问

  • 缺点:

消息消耗将首先读取消耗队列,然后提交日志。在最坏的情况下,此过程会带来一定的成本。
提交日志和使用队列在逻辑上需要保持一致,这给编程模型带来了额外的复杂性

在消息的存储上,RocketMQ与Kafka的主要区别

在消息的存储上,RocketMQ与Kafka的主要区别在于,RocketMQ将所有消息存储在同一个CommitLog中且ConsumeQueue中每个消息只存储20个字节的消息位置信息

而Kafka将每个partition的消息分开存储,这导致RocketMQ单个broker能支持更多的topic和partition。

因为在RocketMQ中,所有消息都存储在同一个文件中,这使得RocketMQ的消息存储是磁盘的顺序写,顺序IO可以接近内存的速度。

**而kafka将消息按partition存储在不同的文件中,因此kafka在消息存储上是随机IO,磁盘的顺序IO要比随机IO快得多。**将partition的数量非常大时,kafka中的随机IO将非常多,这将导致kafka在所有topic的partition变大了之后broker性能会明显下降。

Kafka消息比较
在这里插入图片描述

但是RocketMQ的ConsumeQueue也是随机IO,为何相比kafka能支持更多的partition呢,原因是RocketMQ通过MappedFile的方式读写ConsumeQueue,操作系统对内存映射文件有page cache而ConsumeQueue中的数据都非常小(只有20bytes),读写几乎都是page cache的操作,因此虽然是随机IO但效率也非常高。

问题汇总

1. 订阅消息

消息过滤

消费订阅时Tag用星号(*)或者为(“”) 表示订阅某Topic下所有类型的消息

在这里插入图片描述

https://help.aliyun.com/document_detail/29543.html?spm=5176.11065259.1996646101.searchclickresult.6e024c0c8h87jG

2. rocketmq事务消息,消费者失败了(事务失败)怎么办?

rocketmq的事务消息只保证本地事务和消息发送的原子性,不管消息消费失败的场景

3.如何保证消息不丢失?

  • 1.消息持久化

  • 2.ACK确认机制

  • 3.设置集群镜像模式

  • 4.消息补偿机制

    消息补偿机制核心 : 发现未成功消费的消息, 并且重新发送消息

    消息回调检查服务 : 发送正常消息同时发送一个延迟消息, 当监听到延迟消息的时候, 检查MDB中是否有消费记录 , 如果没有代表存在消息丢失, 重新发送消息

    消息定时检查服务 : 设置定时任务, 定时比对业务DB和MDB ,中的数据是否一致, 如果不一致一定存在消息丢失, 重新发送

4. 如何查消息 indexFile

如果我们需要根据消息ID,来查找消息,consumequeue 中没有存储消息ID,如果不采取其他措施,又得遍历 commitlog文件了,indexFile就是为了解决这个问题的文件

5.RocketMQ如何顺序消费?

因为发送消息的时候,消息发送默认是会采用轮询的方式发送到不通的queue(分区)。如图:
在这里插入图片描述

而消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费。

在这里插入图片描述

但是同一条queue里面,RocketMQ的确是能保证FIFO的。要做到顺序消息,需要把消息确保投递到同一条queue

投递消费使用sharing_key,使消息投递到一个分区

6.消费控速实现

RocketMQ 有序消费时,单个 Queue 只能分配一个 Worker 进行消费,只有当前 Queue 上一个消息成功处理后,才会处理下一个消息,消费速度受限于Queue 的数量和单个消息的处理时延;

无序消费时,所有 Worker 共用一个缓冲区,随机消费不同 Queue 的消息,Worker 之间并发处理消息,Worker 数量越多消费速度越快。

总结:rocketMq和kafka的架构区别

之前阿里巴巴也是使用ActiveMQ,随着业务发展,ActiveMQ IO 模块出现瓶颈,后来阿里巴巴通过一系列优化但是还是不能很好的解决,之后阿里巴巴把注意力放到了主流消息中间件kafka上面,但是kafka并不能满足他们的要求,尤其是低延迟和高可靠性。

所以RocketMQ是站在巨人的肩膀上(kafka),又对其进行了优化让其更满足互联网公司的特点。它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。 RocketMQ目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。

性能与文件布局

RocketMQ QPS没有kafka高,但是rmq可以支持海量的主题,而对于kafka,主题越多性能越差,随机io越严重。所以rmq适合业务消息,kafka适合大数据。场景决定技术转型。

数据可靠性

RocketMQ支持异步实时刷盘,同步刷盘,同步复制,异步复制

kafka使用异步刷盘方式,异步复制/同步复制

总结:RocketMQ的同步刷盘在单机可靠性上比Kafka更高,不会因为操作系统Crash,导致数据丢失。 同时同步Replication也比Kafka异步Replication更可靠,数据完全无单点。另外Kafka的Replication以topic为单位,支持主机宕机,备机自动切换,但是这里有个问题,由于是异步Replication,那么切换后会有数据丢失,同时Leader如果重启后,会与已经存在的Leader产生数据冲突。

开源版本的RocketMQ不支持Master宕机,Slave自动切换为Master,阿里云版本的RocketMQ支持自动切换特性。

文件布局对比

Kafka 中文件的布局是以 Topic/partition ,每一个分区一个物理文件夹,在分区文件级别实现文件顺序写,如果一个Kafka集群中拥有成百上千个主题,每一个主题拥有上百个分区,消息在高并发写入时,其IO操作就会显得零散,其操作相当于随机IO,即 Kafka 在消息写入时的IO性能会随着 topic 、分区数量的增长,其写入性能会先上升,然后下降。

而 RocketMQ在消息写入时追求极致的顺序写,所有的消息不分主题一律顺序写入 commitlog 文件,并不会随着 topic 和 分区数量的增加而影响其顺序性。但通过笔者的实践来看一台物理机并使用SSD盘,但一个文件无法充分利用磁盘IO的性能。

性能对比

  • kafka 单机写入TPS约在百万条/秒,消息大小10个字节

  • RocketMQ单机写入TPS单实例约7万条/秒,单机部署3个Broker,可以跑到最高12万条/秒,消息大小10个字节

总结:Kafka的TPS跑到单机百万,主要是由于Producer端将多个小消息合并,批量发向Broker。 RocketMQ为什么没有这么做?

制片人通常使用的Java语言,缓存过多消息,GC是个很严重的问题

Producer调用发送消息接口,消息未发送到Broker,向业务返回成功,此时Producer宕机,会导致消息丢失,业务出错

rocketmq 消息特性

消费失败重试

kafka消费失败不支持重试。

RocketMQ消费失败支持定时重试,每次重试间隔时间顺延

总结:例如充值类应用,当前时刻调用运营商网关,充值失败,可能是对方压

力过多,稍后再调用就会成功,如支付宝到银行扣款也是类似需求。

这里的重试需要可靠的重试,即失败重试的消息不因为Consumer宕机导致丢失。

支持消息查询

kafka不支持消息查询

RocketMQ支持根据消息标识查询消息,也支持根据消息内容查询消息(发送消息时指定一个消息密钥,任意字符串,例如指定为订单编号)

消息回溯

kafka理论上可以按照偏移来回溯消息

RocketMQ支持按照时间来回溯消息,精度毫秒,例如从一天之前的某时某分某秒开始重新消费消息

总结:典型业务场景如consumer做订单分析,但是由于程序逻辑或者依赖的系统发生故障等原因,导致今天消费的消息全部无效,需要重新从昨天零点开始消费,那么以时间为起点的消息重放功能对于业务非常有帮助。

消息类型的区别

严格的消息顺序

卡夫卡支持消息顺序,但是一台代理宕机后,就会产生消息乱序

RocketMQ支持严格的消息顺序,在顺序消息场景下,一台Broker宕机后,发送消息会失败,但是不会乱序

定时消息

kafka不支持定时消息

RocketMQ支持两类定时消息

开源版本RocketMQ仅支持定时级别,定时级用户可定制

阿里云MQ指定的毫秒级别的延时时间

分布式事务消息

kafka不支持分布式事务消息

阿里云MQ支持分布式事务消息,未来开源版本的RocketMQ也有计划支持分布式事务消息

关于吞吐量
  • kafka在消息存储过程中会根据topic和partition的数量创建物理文件,也就是说我们创建一个topic并指定了3个partition,那么就会有3个物理文件目录,也就说说partition的数量和对应的物理文件是一一对应的。

  • rocketMq在消息存储方式就一个物流问题,也就说传说中的commitLog,rocketMq的queue的数量其实是在consumeQueue里面体现的,在真正存储消息的commitLog其实就只有一个物理文件。

  • kafka的多文件并发写入 VS rocketMq的单文件写入,性能差异kafka完胜可想而知。

  • kafka的大量文件存储会导致一个问题,也就说在partition特别多的时候,磁盘的访问会发生很大的瓶颈,毕竟单个文件看着是append操作,但是多个文件之间必然会导致磁盘的寻道。

kafka

rocketmq

问题

1、重复消息的解决方案

  • 1.消费端处理消息的业务逻辑保持幂等性
    原理是只要第二三四条消息执行时对项目不会造成影响,那么就不需要去管它。例如此消息为判断true或false,可以利用redis bitmap来判断

  • 2.保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现。
    原理是利用一张日志表来记录已经处理成功的消息ID,如果新来的消息ID已经在日志表中,那么就不再处理这条消息

RocketMQ不保证消息不重复,重复时如果由消息系统来处理会对消息系统的吞吐量和高可用有影响,所以需要在业务的进行消息去重操作。

2.怎么保证消息百分百成功

  • 1.消息生产者把消息发送给MQ,如果接收成功,MQ会返回一个ack消息给生产者

  • 2、如果消息接收不成功,MQ会返回一个nack消息给生产者
    阿里云RockerMQ有消息16次重试机制,及死信队列

    碰到上面的问题,我们首先第一会想到解决方案就是发送方在消息发送到MQ之前,将消息给写到硬盘上,这样即使MQ服务器宕机,也不会导致未消费的消息丢失.
    如订单id发送前同步mysql数据库,redis bitmap设置订单uid 为1是发送中,mq消费成功后删除偏移量,hash也可以

3.补偿机制

扫描将持久化在数据库的数据,将一定时间段未成功消费的数据放进mq中,尝试消费
在这里插入图片描述

一般成熟的系统中,对于级别较高的服务和接口,整体的可用性通常都会很高。如果有些业务由于瞬时的网络故障或调用超时等问题,那么这种重试机制其实是非常有效的。

当然,考虑个比较极端的场景,假如系统自身有bug或者程序逻辑有问题,那么重试1W次那也是无济于事的。那岂不是就发生了“明明已经付款,却显示未付款不发货”类似的悲剧?

其实为了交易系统更可靠,我们一般会在类似交易这种高级别的服务代码中,加入详细日志记录的,一旦系统内部引发类似致命异常,会有邮件通知。同时,后台会有定时任务扫描和分析此类日志,检查出这种特殊的情况,会尝试通过程序来补偿并邮件通知相关人员。

在某些特殊的情况下,还会有“人工补偿”的,这也是最后一道屏障。
https://blog.csdn.net/zhejingyuan/article/details/79480128

4.RocketMQ 消息积压了,增加消费者有用吗?

如果消费者的数量小于 MessageQueue 的数量,增加消费者可以加快消息消费速度,减少消息积压。比如一个 Topic 有 4 个 MessageQueue,2 个消费者进行消费,如果增加一个消费者,明细可以加快拉取消息的频率

在这里插入图片描述

如果消费者的数量大于等于 MessageQueue 的数量,增加消费者是没有用的。比如一个 Topic 有 4 个 MessageQueue,并且有 4 个消费者进行消费

在这里插入图片描述

https://zhuanlan.zhihu.com/p/480762345?utm_source=wechat_session&utm_medium=social&utm_oi=990969567522430976

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐