RocketMq介绍
RocketMQ是阿里开源的消息中间件,它是纯java开发,具有低延迟、高吞吐量、高可用性和适合大规模分布式系统应用的特点。从名字可以看出Rocket火箭,代表RocketMQ主打速度。RocketMQ思路起源于Kafka,它对消息的可靠传输及事务性做了优化。
消息中间件作用
削峰, 异步, 解耦
几种消息中间件技术选型
Redis
本身支持MQ功能,所以完全可以当做一个轻量级的队列服务来使用。
但在实际应用中,大家在考虑消息中间件的时候一般都不考虑 Redis。两个原因,一方面是数据大小超过 10K 速度很慢,另一个问题是 Redis 给人的印象就是做缓存的。基于上面这两点原因,Redis 更适合用来做很小规模、业务简单的消息队列场景。 如果业务复杂、业务规模大,一般情况下 Redis 就会被排除。
RocketMq架构图
RocketMq术语
Producer
消息生产者,生产者的作用就是将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ。 (Producer Group生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。)
Consumer
消息消费者,简单来说,消费 MQ 上的消息的应用程序就是消费者,至于消息是否进行逻辑处理,还是直接存储到数据库等取决于业务需要。(Consumer Group消费者组,和生产者组类似,消费同一类消息的多个 consumer 实例组成一个消费者组。)
Topic
Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,那么就需要进行分类,一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息。
Message
Message 是消息的载体。一个 Message 必须指定 topic,相当于寄信的地址。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 broker 上的消息,方便在开发过程中诊断问题。
Tag
标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。
Broker
Broker 是 RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。
Name Server
Name Server 为 producer 和 consumer 提供路由信息。
NameServer是没有状态的,即NameServer中的Broker和topic等状态信息(通过其他角色上报获取)都是保存在内存中的,不会持久化存储(可通过配置实现),集群可以横向扩展。主要功能如下:
a.接收Broker(master和slave)启动时的注册路由信息;
b.为producer和consumer提供路由服务,即通过topic名字获取所有broker的路由信息
c.接收broker发送的心跳信息,如果心跳的时间戳过期NameServer关闭与broker的连接。
RocketMq刷盘策略
RocketMQ 的所有消息都是持久化的,先写入系统 PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。
RocketMQ 提供了同步刷盘和异步刷盘两种机制。默认使用异步刷盘机制。
异步刷盘(ASYNC_FLUSH)
返回成功状态时,消息只是被写入内存 pagecache,写操作返回快,吞吐量达,当内存里的消息积累到一定程度时,统一出发写磁盘动作,快速写入。同步刷盘(SYNC_FLUSH)
返回成功状态时,消息已经被写入磁盘。
消息写入内存 pagecache 后,立即通知刷盘线程,刷盘完成后,返回消息写成功的状态。
同步刷盘与异步刷盘的唯一区别是异步刷盘写完 pagecache 直接返回,而同步刷盘需要等待刷盘完成才返回
RocketMq复制策略
同步复制(SYNC_MASTER)
master 和 slave 都写成功后返回成功状态。好处是如果master出故障,slave上有全部备份,容易恢复。缺点是增大延迟,降低吞吐量。
异步复制(ASYNC_MASTER)
只要 master 写成功就返回成功状态。好处是低延迟、高吞吐,缺点是如果 master 出故障,数据没有写入 slave,就会有丢失。
推荐策略: 异步刷盘(ASYNC_FLUSH) + 同步复制(SYNC_MASTER)。
Rocketmq消息存储
顺序写,随机读.
消息存储是由ConsumeQueue和CommitLog配合完成的。一个Topic里面有多个MessageQueue,每个MessageQueue对应一个ConsumeQueue.
默认地址:store/consumequeue/{topicName}/{queueid}/fileName
ConsumeQueue里记录着消息物理存储地址。(读:consumer根据消息的consumeQueue找到消息存储具体路径,从而读取里面信息)
CommitLog就存储文件具体的字节信息。(写:文件大小默认1g,文件名称20位数 左边补0右边为偏移量。消息顺序写入文件,文件满了则写入下一个文件)
ZeroCopy零拷贝
Consumer 消费消息过程,使用了零拷贝,零拷贝包含以下两种方式:
零拷贝技术有mmap及sendfile;sendfile把文件复制给了内核缓冲区, 实现用户态内核态共享 ,当大文件传输时快,
mmap做了用户进程和文件的映射关系, 实现用户态内核态共享, 当小文件传输快。
MMQ发送的消息通常都很小,rocketmq就是以mmap+write方式实现的。像kafka、netty都采用了零拷贝技术。
Rocketmq的网络模型
RocketMQ 使用 Netty 框架实现高性能的网络传输。Netty 主要特点:
a具有统一的 API,用户无需关心 NIO 的编程模型和概念。通过 Netty 的 ChannelHandler 可以对通信框架进行灵活的定制和扩展。
b Netty 封装了网络编程涉及基本功能:拆包解包、异常检测、零拷贝传输。
c Netty 解决了 NIO 的 Epoll Bug,避免空轮询导致 CPU 的 100% 利用率。
d 支持多种 Reactor 线程模型。
e使用范围广泛,有较好的的开源社区支持。Hadoop、Spark、Dubbo 等项目都集成 Netty。
RocketMq保证消息顺序
实际上,RocketMQ是支持顺序消费的。 但这个顺序,不是全局顺序,只是分区顺序。要全局顺序只能一个分区。 之所以出现你这个场景看起来不是顺序的,是因为发送消息的时候,消息发送默认是会采用轮询的方式发送到不通的queue(分区)。如图:
要做到顺序消息,就把消息确保投递到同一条queue。顺序消费会投递到同一个queue,同一个queue肯定会投递到同一个消费实例。
RocketMq保证消息零丢失
可能消息丢失的3种场景, 如下图所示:
如何保证消息的零丢失:
从Producer分析, 确保消息正确的发送到了Broker:
1.默认情况下,可以通过同步的方式阻塞式的发送,check SendStatus,状态是OK,表示消息一定成功的投递到了Broker,状态超时或者失败,则会触发默认的2次重试。此方法的发送结果,可能Broker存储成功了,也可能没成功
2.RocketMQ提供了ack机制,以保证消息能够被正常消费。发送者为了保证消息肯定消费成功,只有使用方明确表示消费成功,RocketMQ才会认为消息消费成功。中途断电,抛出异常等都不会认为成功——即都会重新投递。
采取事务消息的投递方式,并不能保证消息100%投递成功到了Broker,但是如果消息发送Ack失败的话,此消息会存储在CommitLog当中,但是对ConsumerQueue是不可见的。可以在日志中查看到这条异常的消息,严格意义上来讲,也并没有完全丢失
3.RocketMQ支持 日志的索引,如果一条消息发送之后超时,也可以通过查询日志的API,来check是否在Broker存储成功
从Broker分析, 如何保证消息不丢失:
1.消息支持持久化到Commitlog里面,即使宕机后重启,未消费的消息也是可以加载出来的
2.Broker自身支持同步刷盘、异步刷盘的策略,可以保证接收到的消息一定存储在本地的内存中
3.Broker集群支持 1主N从的策略,支持同步复制和异步复制的方式,同步复制可以保证即使Master 磁盘崩溃,消息仍然不会丢失
cunmser分析, 如何保证消息不丢失:
1.Consumer自身维护一个持久化的offset(对应MessageQueue里面的min offset),标记已经成功消费或者已经成功发回到broker的消息下标
2.如果Consumer消费失败,那么它会把这个消息发回给Broker,发回成功后,再更新自己的offset
3.如果Consumer消费失败,发回给broker时,broker挂掉了,那么Consumer会定时重试这个操作
4.如果Consumer和broker一起挂了,消息也不会丢失,因为consumer 里面的offset是定时持久化的,重启之后,继续拉取offset之前的消息到本地
rocketMq事务消息
RocketMQ三种事务状态
CommitTransaction:消息提交,当消息状态为CommitTransaction,表示允许消费者允许消费当前消息.
RollbackTransaction:消息回滚,表示MQ服务端将会删除当前半消息,不允许消费者消费。
Unknown:中间状态,表示MQ服务需要发起回查操作,检测当前发送方本地事务的执行状态。
RocketMQ如何实现事务消息
生产者发送Prepare消息到Broker(事务消息的发送为同步方式)。
Broker收到消息后,将消息写到Half Topic(RMQ_SYS_TRANS_HALF_TOPIC),写入成功后给生产者返回成功响应。
生产者获取到该消息的事务ID后,开始执行本地事务。
本地事务执行成功后提交Commit,失败则提交Rollback。Broker收到Commit或Rollback消息后,将消息写到OP Topic(RMQ_SYS_TRANS_OP_HALF_TOPIC)该Topic存放着Prepare消息对应的Commit/Rollback消息,Broker利用Half Topic和 OP Topic 可以计算出需要回查的消息。如果是Commit消息,Broker会将消息存到真正的Topic里,从而消费者可以正常消费消息。如果是Rollback消息,Broker会删除Prepare消息而不进行下发,然后生产者根据业务要求决定是否进行其他的回滚操作。
如果本地事务执行超时提交或者提交Unknow状态则会触发broker的事务回查功能。
事务消息投递的三个Topic
Half Topic:用于记录所有的Prepare消息。
Op(Operation ) Half Topic:记录已经提交了状态的Prepare消息。
Real Topic:事务消息真正的Topic,在Commit后会才会将消息写入该Topic,从而进行消息的投递。