RocketMQ 5.x消息不丢失核心配置allAckInSyncStateSet详解

这里是weihubeats ,觉得文章不错可以关注公众号小奏技术

背景

最近在研究RocketMQ消息不丢失的一些细节,所以关注到了5.x的一个新参数allAckInSyncStateSet

消息一个不丢的代价很大,我们需要再消息丢失和性能之间找到一个平衡,在保证性能的同时尽量减少消息丢失风险

具体消息的可靠度还是要依据具体业务来做决策

举个🌰

RocketMQ数据刷盘的策略默认是使用异步刷盘,异步刷盘除非服务器真的宕机 我们的数据才会丢失.为了性能我们一般设置异步刷盘,因为消息的丢失风险在合理范围(业务可接受)

RocketMQ宕机并不会丢失,因为存在于操作系统的pageCache里面,而不是JVM内存

主从之间的数据同步,也是有很多配置供我们选择。 比如有如下一些配置

  • checkSyncStateSetPeriod:检查 SyncStateSet 的时间间隔,检查 SyncStateSet 可能会 shrink SyncState。默认5000(5s)
  • haMaxTimeSlaveNotCatchup:表示 Slave 没有跟上 Master 的最大时间间隔,若在 SyncStateSet 中的 slave 超过该时间间隔会将其从 SyncStateSet 移除。默认为 15000(15s)。
  • inSyncReplicas:需保持同步的副本组数量,默认为1,allAckInSyncStateSet=true 时该参数无效。
  • minInSyncReplicas:最小需保持同步的副本组数量,若 SyncStateSet 中副本个数小于 minInSyncReplicas 则 putMessage 直接返回 PutMessageStatus.IN_SYNC_REPLICAS_NOT_ENOUGH,默认为1。
  • allAckInSyncStateSet:若该值为 true,则一条消息需要复制到 SyncStateSet 中的每一个副本才会向客户端返回成功,可以保证消息不丢失。默认为 false。

参考官网 官网:rocketmq.apache.org/zh/docs/dep...

合理的设置这些参数才能尽可能在保证性能的同时减少在宕机时消息的丢失

allAckInSyncStateSet设置的疑惑

其中我们可以看到一个很关键的参数allAckInSyncStateSet

官方的说法就是若该值为 true,则一条消息需要复制到 SyncStateSet 中的每一个副本才会向客户端返回成功,可以保证消息不丢失。默认为 false

那么就有如下几个疑惑了

每一个副本是指都在SyncStateSet里面的副本吗

比如现在一个集群有三个节点

  • Broker-a-master
  • Broker-a-slave
  • broker-a-slave

此时的SyncStateSet中的数据

在同步中的broker

  • Broker-a-master 192.168.1.1
  • Broker-a-slave 192.168.1.2
  • broker-a-slave 192.168.1.3

如果此时broker-a-master宕机。随机选出一个slave broker切换成master

此时的SyncStateSet数据 在同步中的broker

  • Broker-a-master 192.168.1.2
  • Broker-a-slave 192.168.1.3 不在同步中的broker
  • broker-a-slave 192.168.1.1

那么此时宕机的master(192.168.1.1)还需要返回ack才算消息消息写入成功吗

如果是,那么整个broker-a集群处于无法写入的状态,也无法恢复,对整个集群的可用性影响太大了

最大需要间隔多久才能将宕机的master移出到不在同步中的broker呢?

所以疑惑就有亮点

  1. 返回ack是所有存活的broker还是在同步中的broker
  2. 什么时候将异常的broker移出在同步中的broker

下面我们就上面的2个问题我们结合源码分析来找到答案

allAckInSyncStateSet在何处使用

我们全局搜索allAckInSyncStateSet在何处使用关键字能直接找到org.apache.rocketmq.store.ha.GroupTransferService#doWaitTransfer中有使用

这里我们可以看到获取的同步副本syncStateSet是通过autoSwitchHAService.getSyncStateSet()获取的

所以判断需要返回多少个ack主要是基于syncStateSet判断的。 即AutoSwitchHAServicesyncStateSet属性

何时调用org.apache.rocketmq.store.ha.autoswitch.AutoSwitchHAService#setSyncStateSet方法来更新syncStateSet

可以看到有三处调用

  • changeSyncStateSet
  • schedulingSyncBrokerMetadata
  • doReportSyncStateSetChanged

我们先看看doReportSyncStateSetChanged这个方法

doReportSyncStateSetChanged

org.apache.rocketmq.broker.controller.ReplicasManager#doReportSyncStateSetChanged

可以看到这里主要是与controller 进行通信。

我们查看请求状态码为CONTROLLER_ALTER_SYNC_STATE_SET

然后拿取到controller的元数据结果进行本地同步。应该是没有相关的同步状态下线操作。我们继续看看schedulingSyncBrokerMetadata

schedulingSyncBrokerMetadata

我们来看看org.apache.rocketmq.broker.controller.ReplicasManager#schedulingCheckSyncStateSet方法

这里我们看到一个很关键的方法org.apache.rocketmq.store.ha.autoswitch.AutoSwitchHAConnection#maybeExpandInSyncStateSet

java 复制代码
public Set<String> maybeShrinkInSyncStateSet() {
        final Set<String> newSyncStateSet = getSyncStateSet();
        final long haMaxTimeSlaveNotCatchup = this.defaultMessageStore.getMessageStoreConfig().getHaMaxTimeSlaveNotCatchup();
        for (Map.Entry<String, Long> next : this.connectionCaughtUpTimeTable.entrySet()) {
            final String slaveAddress = next.getKey();
            if (newSyncStateSet.contains(slaveAddress)) {
                final Long lastCaughtUpTimeMs = this.connectionCaughtUpTimeTable.get(slaveAddress);
                if ((System.currentTimeMillis() - lastCaughtUpTimeMs) > haMaxTimeSlaveNotCatchup) {
                    newSyncStateSet.remove(slaveAddress);
                }
            }
        }
        return newSyncStateSet;
    }

lastCaughtUpTimeMs获取值为

java 复制代码
long caughtUpTimeMs = this.haService.getDefaultMessageStore().getMaxPhyOffset() == slaveMaxOffset ? System.currentTimeMillis() : this.lastTransferTimeMs;

即如果slave最大偏移量不等于master最大偏移量,则不更新同步时间。相等则更新同步时间为最新时间

可以看到会与最大不同步时间进行判断haMaxTimeSlaveNotCatchup(默认15s)

15s内slavemaster最大偏移量不同步,则移出syncStateSet

这个检测任务是多久执行一次呢?

可以看到是3s执行一次

所以最多需要15s才能将异常的broker移除在同步状态的broker

总结

总的来说开启allAckInSyncStateSet最多会在master宕机后15s内集群写入不可用,等异常的broker被移出syncStateSet后就可以继续正常写入了。

实际比如有三个集群

  • broker-a
  • broker-b
  • broker-c

一个集群比如broker-a在15s内不可写入也是可以接受的,因为还有两个集群可以写入,product也是有消息重试的。

所以如果不开启allAckInSyncStateSet。一条消息写入master后还没来得及同步给slave就宕机,消息丢失的风险还是太大,线上还是推荐开启allAckInSyncStateSet

相关推荐
uzong2 小时前
技术故障复盘模版
后端
GetcharZp2 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程2 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研2 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi3 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国4 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy4 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack4 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9655 小时前
pip install 已经不再安全
后端
寻月隐君5 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github