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

相关推荐
豌豆花下猫6 分钟前
Python 潮流周刊#78:async/await 是糟糕的设计(摘要)
后端·python·ai
YMWM_8 分钟前
第一章 Go语言简介
开发语言·后端·golang
码蜂窝编程官方25 分钟前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游
hummhumm43 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
J老熊1 小时前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
AuroraI'ncoding1 小时前
时间请求参数、响应
java·后端·spring
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
许苑向上1 小时前
Dubbo集成SpringBoot实现远程服务调用
spring boot·后端·dubbo
郑祎亦2 小时前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis