一、如何保证消息不丢失
这个问题要从Producer,Consumer和Broker三个方面来回答。
从Producer角度分析,如何确保消息成功发送到了Broker?
1、可以采用同步发送,即发送一条数据等到接受者返回响应之后再发送下一个数据包。如果返回响应OK,表示消息成功发送到了broker,状态超时或者失败都会触发二次重试。
2、可以采用分布式事务消息的投递方式。
3、如果一条消息发送之后超时,也可以通过查询日志的API,来检查是否在Broker存储成功。
总的来说,Producer还是采用同步发送来保证的。
从Broker角度分析,如何确保消息持久化?
1、消息只要持久化到CommitLog(日志文件)中,即使Broker宕机,未消费的消息也能重新恢复再消费。
2、Broker的刷盘机制:同步刷盘和异步刷盘,不管哪种刷盘都可以保证消息一定存储在pagecache中(内存中),但是同步刷盘更可靠,它是Producer发送消息后等数据持久化到磁盘之后再返回响应给Producer。
3、Broker支持多Master多Slave同步双写和多Master多Slave异步复制模式,消息都是发送给Master主机,但是消费既可以从Master消费,也可以从Slave消费。同步双写模式可以保证即使Master宕机,消息肯定在Slave中有备份,保证了消息不会丢失。
从Consumer角度分析,如何保证消息被成功消费?
Consumer自身维护了个持久化的offset(对应Message Queue里的min offset),用来标记已经成功消费且已经成功发回Broker的消息下标。如果Consumer消费失败,它会向Broker发回消费失败的状态,发回成功才会更新自己的offset。如果发回给broker时broker挂掉了,Consumer会定时重试,如果Consumer和Broker一起挂掉了,消息还在Broker端存储着,Consumer端的offset也是持久化的,重启之后继续拉取offset之前的消息进行消费。
二、消息重复问题
RocketMQ并没有对消息重复问题做什么策略处理,需要用户在业务上做幂等。
消息队列 RocketMQ 消费者在接收到消息以后,有必要根据业务上的唯一 Key 对消息做幂等处理的必要性。
消息幂等的必要性
在互联网应用中,尤其在网络不稳定的情况下,消息队列 RocketMQ 的消息有可能会出现重复,这个重复简单可以概括为以下情况:
A、发送时消息重复
当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。
B、消费时消息重复
消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。
C、负载均衡时消息重复(包括但不限于网络抖动、Broker 重启以及订阅方应用重启)
当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时,会触发 Rebalance,此时消费者可能会收到重复消息。
三、消息清理机制
Broker 中的消息被消费后会立即删除吗?
不会,每条消息都会持久化到 CommitLog 中,每个 Consumer 连接到 Broker 后会维持消费进度信息,当有消息消费后只是当前Consumer 的消费进度(CommitLog 的 offset)更新了。
当然也可以配置消息被消费后是否立即删除。当消费者成功消费一条消息,并完成确认后,Broker会将这条消息的状态更新为已消费,并根据配置的策略来决定是否立即删除这条消息。
RocketMQ支持两种消息删除策略:
立即删除:当Broker接到消费者的确认消息时,立刻删除该消息。这种删除策略可以在Broker的配置文件中设置,但是一旦设置为立即删除,就无法保证消息的可靠性。
延迟删除(默认删除策略):当Broker接到消费者的确认消息时,将该消息的状态标记为"已消费",但并未立即删除,而是将该消息的删除时间延迟到一定的时间之后,再进行删除。这种删除策略可以使消息在删除前保留一段时间,以便于后续的重试或者其他操作。
未消费的消息是否会被删除?
无论消息是否被消费,RocketMQ的消息保留时间默认为48小时,且不支持修改。RocketMQ消息存储在CommitLog文件中,CommitLog文件大小为1GB,当一个CommitLog文件写满后,会生成一个新的CommitLog文件。RocketMQ删除消息是删除CommitLog文件,而不是删除一条消息。CommitLog文件为顺序写入,当最后写入的一条消息过期时,表示CommitLog文件过期。满足如下任意一个条件,CommitLog文件将会被清理:
- 每天凌晨4点会清理过期的文件,部分老实例由于未设置时区,清理时间为每天中午12点。
- 磁盘占中率达到过期清理警戒线70%时,会立刻清理过期的文件。
- 磁盘占用到达清理警戒线(85%)后,会按照清理规则清理文件,无论是否过期,按照最老的文件开始清理。
- 磁盘占用达到系统危险警戒线(90%)后,broker会拒绝写入