RocketMQ

2 篇文章 0 订阅
1 篇文章 0 订阅

目前主流的MQ主要是Rocketmq、kafka、Rabbitmq,Rocketmq相比于Rabbitmq、kafka具有主要优势特性有:
• 支持事务型消息(消息发送和DB操作保持两方的最终一致性,rabbitmq和kafka不支持)
• 支持结合rocketmq的多个系统之间数据最终一致性(多方事务,二方事务是前提)
• 支持18个级别的延迟消息(rabbitmq和kafka不支持)
• 支持指定次数和时间间隔的失败消息重发(kafka不支持,rabbitmq需要手动确认)
• 支持consumer端tag过滤,减少不必要的网络传输(rabbitmq和kafka不支持)
• 支持重复消费(rabbitmq不支持,kafka支持)

Rocketmq、kafka、Rabbitmq的详细对比,请参照下表格:

生产者

发送消息注意事项

Tag的使用

一个应用尽可能用一个Topic,而消息子类型则可以用tags来标识。tags可以由应用自由设置,只有生产者在发送消息设置了tags,消费方在订阅消息时才可以利用tags通过broker做消息过滤,5.x SDK 可以调用messageBuilder.setTag("messageTag"),历史版本可以调用 message.setTags("messageTag")。

Keys的使用

每个消息在业务层面一般建议映射到业务的唯一标识并设置到keys字段,方便将来定位消息丢失问题。服务器会为每个消息创建索引(哈希索引),应用可以通过topic、key来查询这条消息内容,以及消息被谁消费。由于是哈希索引,请务必保证key尽可能唯一,这样可以避免潜在的哈希冲突。常见的设置策略使用订单Id、用户Id、请求Id等比较离散的唯一标识来处理。

日志的打印

消息发送成功或者失败要打印消息日志,用于业务排查问题。Send消息方法只要不抛异常,就代表发送成功。

消息发送失败处理方式

Producer的send方法本身支持内部重试,5.x SDK的重试逻辑参考发送重试策略

以上策略也是在一定程度上保证了消息可以发送成功。如果业务要求消息发送不能丢,仍然需要对可能出现的异常做兜底,比如调用send同步方法发送失败时,则尝试将消息存储到db,然后由后台线程定时重试,确保消息一定到达Broker。

上述DB重试方式为什么没有集成到MQ客户端内部做,而是要求应用自己去完成,主要基于以下几点考虑:首先,MQ的客户端设计为无状态模式,方便任意的水平扩展,且对机器资源的消耗仅仅是cpu、内存、网络。其次,如果MQ客户端内部集成一个KV存储模块,那么数据只有同步落盘才能较可靠,而同步落盘本身性能开销较大,所以通常会采用异步落盘,又由于应用关闭过程不受MQ运维人员控制,可能经常会发生 kill -9 这样暴力方式关闭,造成数据没有及时落盘而丢失。第三,Producer所在机器的可靠性较低,一般为虚拟机,不适合存储重要数据。综上,建议重试过程交由应用来控制。

消费者

消费过程幂等

RocketMQ 无法避免消息重复(Exactly-Once),所以如果业务对消费重复非常敏感,务必要在业务层面进行去重处理。可以借助关系数据库进行去重。首先需要确定消息的唯一键,可以是msgId,也可以是消息内容中的唯一标识字段,例如订单Id等。在消费之前判断唯一键是否在关系数据库中存在。如果不存在则插入,并消费,否则跳过。(实际过程要考虑原子性问题,判断是否存在可以尝试插入,如果报主键冲突,则插入失败,直接跳过)

msgId一定是全局唯一标识符,但是实际使用中,可能会存在相同的消息有两个不同msgId的情况(消费者主动重发、因客户端重投机制导致的重复等),这种情况就需要使业务字段进行重复消费。

消费速度慢的处理方式

提高消费并行度

绝大部分消息消费行为都属于 IO 密集型,即可能是操作数据库,或者调用 RPC,这类消费行为的消费速度在于后端数据库或者外系统的吞吐量,通过增加消费并行度,可以提高总的消费吞吐量,但是并行度增加到一定程度,反而会下降。所以,应用必须要设置合理的并行度。 如下有几种修改消费并行度的方法:

  • 同一个 ConsumerGroup 下,通过增加 Consumer 实例数量来提高并行度。可以通过加机器,或者在已有机器启动多个进程的方式。
  • 提高单个 Consumer 的消费并行线程,5.x PushConsumer SDK 可以通过PushConsumerBuilder.setConsumptionThreadCount() 设置线程数,SimpleConsumer可以由业务线程自由增加并发,底层线程安全;历史版本SDK PushConsumer可以通过修改参数 consumeThreadMin、consumeThreadMax实现。

批量方式消费

某些业务流程如果支持批量方式消费,则可以很大程度上提高消费吞吐量,例如订单扣款类应用,一次处理一个订单耗时 1 s,一次处理 10 个订单可能也只耗时 2 s,这样即可大幅度提高消费的吞吐量。建议使用5.x SDK的SimpleConsumer,每次接口调用设置批次大小,一次性拉取消费多条消息。

重置位点跳过非重要消息

发生消息堆积时,如果消费速度一直追不上发送速度,如果业务对数据要求不高的话,可以选择丢弃不重要的消息。建议使用重置位点功能直接调整消费位点到指定时刻或者指定位置。

优化每条消息消费过程

举例如下,某条消息的消费过程如下:

  • 根据消息从 DB 查询【数据 1】
  • 根据消息从 DB 查询【数据 2】
  • 复杂的业务计算
  • 向 DB 插入【数据 3】
  • 向 DB 插入【数据 4】

这条消息的消费过程中有4次与 DB的 交互,如果按照每次 5ms 计算,那么总共耗时 20ms,假设业务计算耗时 5ms,那么总过耗时 25ms,所以如果能把 4 次 DB 交互优化为 2 次,那么总耗时就可以优化到 15ms,即总体性能提高了 40%。所以应用如果对时延敏感的话,可以把DB部署在SSD硬盘,相比于SCSI磁盘,前者的RT会小很多。

消费打印日志

如果消息量较少,建议在消费入口方法打印消息,消费耗时等,方便后续排查问题。

   new MessageListener() {
        @Override
        public ConsumeResult consume(MessageView messageView) {
            LOGGER.info("Consume message={}", messageView);
            //Do your consume process
            return ConsumeResult.SUCCESS;
            }
    }

如果能打印每条消息消费耗时,那么在排查消费慢等线上问题时,会更方便。但如果线上环境TPS很高,不建议开启,避免日志太多影响性能。

Broker

Broker 角色

Broker 角色分为 ASYNC_MASTER(异步主机)、SYNC_MASTER(同步主机)以及SLAVE(从机)。如果对消息的可靠性要求比较严格,可以采用 SYNC_MASTER加SLAVE的部署方式。如果对消息可靠性要求不高,可以采用ASYNC_MASTER加SLAVE的部署方式。如果只是测试方便,则可以选择仅ASYNC_MASTER或仅SYNC_MASTER的部署方式。

FlushDiskType

SYNC_FLUSH(同步刷新)相比于ASYNC_FLUSH(异步处理)会损失很多性能,但是也更可靠,所以需要根据实际的业务场景做好权衡。

Broker 配置

参数名默认值说明
listenPort10911接受客户端连接的监听端口
namesrvAddrnullnameServer 地址
brokerIP1网卡的 InetAddress当前 broker 监听的 IP
brokerIP2跟 brokerIP1 一样存在主从 broker 时,如果在 broker 主节点上配置了 brokerIP2 属性,broker 从节点会连接主节点配置的 brokerIP2 进行同步
brokerNamenullbroker 的名称
brokerClusterNameDefaultCluster本 broker 所属的 Cluser 名称
brokerId0broker id, 0 表示 master, 其他的正整数表示 slave
storePathCommitLog$HOME/store/commitlog/存储 commit log 的路径
storePathConsumerQueue$HOME/store/consumequeue/存储 consume queue 的路径
mapedFileSizeCommitLog1024 1024 1024(1G)commit log 的映射文件大小
deleteWhen04在每天的什么时间删除已经超过文件保留时间的 commit log
fileReserverdTime72以小时计算的文件保留时间
brokerRoleASYNC_MASTERSYNC_MASTER/ASYNC_MASTER/SLAVE
flushDiskTypeASYNC_FLUSHSYNC_FLUSH/ASYNC_FLUSH SYNC_FLUSH 模式下的 broker 保证在收到确认生产者之前将消息刷盘。ASYNC_FLUSH 模式下的 broker 则利用刷盘一组消息的模式,可以取得更好的性能。

 

Dledger快速搭建

前言

DLedger是一套基于Raft协议的分布式日志存储组件,部署 RocketMQ 时可以根据需要选择使用DLeger来替换原生的副本存储机制。本文档主要介绍如何快速构建和部署基于 DLedger 的可以自动容灾切换的 RocketMQ 集群。

1. 源码构建

构建分为两个部分,需要先构建 DLedger,然后 构建 RocketMQ。

1.1 构建 DLedger

$ git clone https://github.com/openmessaging/dledger.git
$ cd dledger
$ mvn clean install -DskipTests

1.2 构建 RocketMQ

$ git clone https://github.com/apache/rocketmq.git
$ cd rocketmq
$ git checkout -b develop origin/develop
$ mvn -Prelease-all -DskipTests clean install -U

2. 快速部署

在构建成功后

#{rocketmq-version} replace with rocketmq actual version. example: 5.0.0-SNAPSHOT
$ cd distribution/target/rocketmq-{rocketmq-version}/rocketmq-{rocketmq-version}

$ sh bin/dledger/fast-try.sh start

如果上面的步骤执行成功,可以通过 mqadmin 运维命令查看集群状态。

$ sh bin/mqadmin clusterList -n 127.0.0.1:9876

顺利的话,会看到如下内容:

(BID 为 0 的表示 Master,其余都是 Follower)

启动成功,现在可以向集群收发消息,并进行容灾切换测试了。

关闭快速集群,可以执行:

$ sh bin/dledger/fast-try.sh stop

快速部署,默认配置在 conf/dledger 里面,默认的存储路径在 /tmp/rmqstore。

3. 容灾切换

部署成功,杀掉 Leader 之后(在上面的例子中,杀掉端口 30931 所在的进程),等待约 10s 左右,用 clusterList 命令查看集群,就会发现 Leader 切换到另一个节点了。

Dledger集群搭建

本部分主要介绍如何部署自动容灾切换的 RocketMQ-on-DLedger Group。

RocketMQ-on-DLedger Group 是指一组相同名称的 Broker,至少需要 3 个节点,通过 Raft 自动选举出一个 Leader,其余节点 作为 Follower,并在 Leader 和 Follower 之间复制数据以保证高可用。 RocketMQ-on-DLedger Group 能自动容灾切换,并保证数据一致。 RocketMQ-on-DLedger Group 是可以水平扩展的,也即可以部署任意多个 RocketMQ-on-DLedger Group 同时对外提供服务。

1. 新集群部署

1.1 编写配置

每个 RocketMQ-on-DLedger Group 至少准备三台机器(本文假设为 3)。 编写 3 个配置文件,建议参考 conf/dledger 目录下的配置文件样例。 关键配置介绍:

name含义举例
enableDLegerCommitLog是否启动 DLedgertrue
dLegerGroupDLedger Raft Group的名字,建议和 brokerName 保持一致RaftNode00
dLegerPeersDLedger Group 内各节点的端口信息,同一个 Group 内的各个节点配置必须要保证一致n0-127.0.0.1:40911;n1-127.0.0.1:40912;n2-127.0.0.1:40913
dLegerSelfId节点 id, 必须属于 dLegerPeers 中的一个;同 Group 内各个节点要唯一n0
sendMessageThreadPoolNums发送线程个数,建议配置成 Cpu 核数16

这里贴出 conf/dledger/broker-n0.conf 的配置举例。

brokerClusterName = RaftCluster
brokerName=RaftNode00
listenPort=30911
namesrvAddr=127.0.0.1:9876
storePathRootDir=/tmp/rmqstore/node00
storePathCommitLog=/tmp/rmqstore/node00/commitlog
enableDLegerCommitLog=true
dLegerGroup=RaftNode00
dLegerPeers=n0-127.0.0.1:40911;n1-127.0.0.1:40912;n2-127.0.0.1:40913
## must be unique
dLegerSelfId=n0
sendMessageThreadPoolNums=16

1.2 启动 Broker

与老版本的启动方式一致。

$ nohup sh bin/mqbroker -c conf/dledger/xxx-n0.conf & 
$ nohup sh bin/mqbroker -c conf/dledger/xxx-n1.conf & 
$ nohup sh bin/mqbroker -c conf/dledger/xxx-n2.conf & 

2. 旧集群升级

如果旧集群采用 Master 方式部署,则每个 Master 都需要转换成一个 RocketMQ-on-DLedger Group。
如果旧集群采用 Master-Slave 方式部署,则每个 Master-Slave 组都需要转换成一个 RocketMQ-on-DLedger Group。

2.1 杀掉旧的 Broker

可以通过 kill 命令来完成,也可以调用 bin/mqshutdown broker

2.2 检查旧的 Commitlog

RocketMQ-on-DLedger 组中的每个节点,可以兼容旧的 Commitlog ,但其 Raft 复制过程,只能针对新增加的消息。因此,为了避免出现异常,需要保证 旧的 Commitlog 是一致的。 如果旧的集群是采用 Master-Slave 方式部署,有可能在shutdown时,其数据并不是一致的,建议通过md5sum 的方式,检查最近的最少 2 个 Commmitlog 文件,如果发现不一致,则通过拷贝的方式进行对齐。

虽然 RocketMQ-on-DLedger Group 也可以以 2 节点方式部署,但其会丧失容灾切换能力(2n + 1 原则,至少需要3个节点才能容忍其中 1 个宕机)。
所以在对齐了 Master 和 Slave 的 Commitlog 之后,还需要准备第 3 台机器,并把旧的 Commitlog 从 Master 拷贝到 第 3 台机器(记得同时拷贝一下 config 文件夹)。

在 3 台机器准备好了之后,旧 Commitlog 文件也保证一致之后,就可以开始走下一步修改配置了。

2.3 修改配置

参考新集群部署。

2.4 重新启动 Broker

参考新集群部署。

2. 权限控制的定义与属性值

2.1权限定义

对RocketMQ的Topic资源访问权限控制定义主要如下表所示,分为以下四种

权限含义
DENY拒绝
ANYPUB 或者 SUB 权限
PUB发送权限
SUB订阅权限

2.2 权限定义的关键属性

字段取值含义
globalWhiteRemoteAddresses*;192.168.*.*;192.168.0.1全局IP白名单
accessKey字符串Access Key
secretKey字符串Secret Key
whiteRemoteAddress*;192.168.*.*;192.168.0.1用户IP白名单
admintrue;false是否管理员账户
defaultTopicPermDENY;PUB;SUB;PUB|SUB默认的Topic权限
defaultGroupPermDENY;PUB;SUB;PUB|SUB默认的ConsumerGroup权限
topicPermstopic=权限各个Topic的权限
groupPermsgroup=权限各个ConsumerGroup的权限

具体可以参考distribution/conf/plain_acl.yml配置文件

3. 支持权限控制的集群部署

distribution/conf/plain_acl.yml配置文件中按照上述说明定义好权限属性后,打开aclEnable开关变量即可开启RocketMQ集群的ACL特性。这里贴出Broker端开启ACL特性的properties配置文件内容:

brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deleteWhen=04
fileReservedTime=48
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
storePathRootDir=/data/rocketmq/rootdir-a-m
storePathCommitLog=/data/rocketmq/commitlog-a-m
autoCreateSubscriptionGroup=true
## if acl is open,the flag will be true
aclEnable=true
listenPort=10911
brokerIP1=XX.XX.XX.XX1
namesrvAddr=XX.XX.XX.XX:9876

4. 权限控制主要流程

ACL主要流程分为两部分,主要包括权限解析和权限校验。

4.1 权限解析

Broker端对客户端的RequestCommand请求进行解析,拿到需要鉴权的属性字段。 主要包括: (1)AccessKey:类似于用户名,代指用户主体,权限数据与之对应; (2)Signature:客户根据 SecretKey 签名得到的串,服务端再用SecretKey进行签名验证;

4.2 权限校验

Broker端对权限的校验逻辑主要分为以下几步: (1)检查是否命中全局 IP 白名单;如果是,则认为校验通过;否则走 2; (2)检查是否命中用户 IP 白名单;如果是,则认为校验通过;否则走 3; (3)校验签名,校验不通过,抛出异常;校验通过,则走 4; (4)对用户请求所需的权限 和 用户所拥有的权限进行校验;不通过,抛出异常; 用户所需权限的校验需要注意已下内容: (1)特殊的请求例如 UPDATE_AND_CREATE_TOPIC 等,只能由 admin 账户进行操作; (2)对于某个资源,如果有显性配置权限,则采用配置的权限;如果没有显性配置权限,则采用默认的权限;

5. 热加载修改后权限控制定义

RocketMQ的权限控制存储的默认实现是基于yml配置文件。用户可以动态修改权限控制定义的属性,而不需重新启动Broker服务节点。

6. 权限控制的使用限制

(1)如果ACL与高可用部署(Master/Slave架构)同时启用,那么需要在Broker Master节点的distribution/conf/plain_acl.yml配置文件中 设置全局白名单信息,即为将Slave节点的ip地址设置至Master节点plain_acl.yml配置文件的全局白名单中。

(2)如果ACL与高可用部署(多副本Dledger架构)同时启用,由于出现节点宕机时,Dledger Group组内会自动选主,那么就需要将Dledger Group组 内所有Broker节点的plain_acl.yml配置文件的白名单设置所有Broker节点的ip地址。

7. ACL mqadmin配置管理命令

7.1 更新ACL配置文件中“account”的属性值

该命令的示例如下:

$ sh mqadmin updateAclConfig -n 192.168.1.2:9876 -b 192.168.12.134:10911 -a RocketMQ -s 1234567809123 -t topicA=DENY,topicD=SUB -g groupD=DENY,groupB=SUB

说明:如果不存在则会在ACL Config YAML配置文件中创建;若存在,则会更新对应的“accounts”的属性值; 如果指定的是集群名称,则会在集群中各个broker节点执行该命令;否则会在单个broker节点执行该命令。

参数取值含义
neg:192.168.1.2:9876namesrv地址(必填)
ceg:DefaultCluster指定集群名称(与broker地址二选一)
beg:192.168.12.134:10911指定broker地址(与集群名称二选一)
aeg:RocketMQAccess Key值(必填)
seg:1234567809123Secret Key值(可选)
meg:true是否管理员账户(可选)
weg:192.168.0.*whiteRemoteAddress,用户IP白名单(可选)
ieg:DENY;PUB;SUB;PUB|SUBdefaultTopicPerm,默认Topic权限(可选)
ueg:DENY;PUB;SUB;PUB|SUBdefaultGroupPerm,默认ConsumerGroup权限(可选)
teg:topicA=DENY,topicD=SUBtopicPerms,各个Topic的权限(可选)
geg:groupD=DENY,groupB=SUBgroupPerms,各个ConsumerGroup的权限(可选)

7.2 删除ACL配置文件里面的对应“account”

该命令的示例如下:

$ sh mqadmin deleteAccessConfig -n 192.168.1.2:9876 -c DefaultCluster -a RocketMQ

说明:如果指定的是集群名称,则会在集群中各个broker节点执行该命令;否则会在单个broker节点执行该命令。 其中,参数"a"为Access Key的值,用以标识唯一账户id,因此该命令的参数中指定账户id即可。

参数取值含义
neg:192.168.1.2:9876namesrv地址(必填)
ceg:DefaultCluster指定集群名称(与broker地址二选一)
beg:192.168.12.134:10911指定broker地址(与集群名称二选一)
aeg:RocketMQAccess Key的值(必填)

7.3 更新ACL配置文件里面中的全局白名单

该命令的示例如下:

$ sh mqadmin updateGlobalWhiteAddr -n 192.168.1.2:9876 -b 192.168.12.134:10911 -g 10.10.154.1,10.10.154.2

说明:如果指定的是集群名称,则会在集群中各个broker节点执行该命令;否则会在单个broker节点执行该命令。 其中,参数"g"为全局IP白名的值,用以更新ACL配置文件中的“globalWhiteRemoteAddresses”字段的属性值。

参数取值含义
neg:192.168.1.2:9876namesrv地址(必填)
ceg:DefaultCluster指定集群名称(与broker地址二选一)
beg:192.168.12.134:10911指定broker地址(与集群名称二选一)
geg:10.10.154.1,10.10.154.2全局IP白名单(必填)

7.4 查询集群/Broker的ACL配置文件版本信息

该命令的示例如下:

$ sh mqadmin clusterAclConfigVersion -n 192.168.1.2:9876 -c DefaultCluster

说明:如果指定的是集群名称,则会在集群中各个broker节点执行该命令;否则会在单个broker节点执行该命令。

参数取值含义
neg:192.168.1.2:9876namesrv地址(必填)
ceg:DefaultCluster指定集群名称(与broker地址二选一)
beg:192.168.12.134:10911指定broker地址(与集群名称二选一)

7.5 查询集群/Broker的ACL配置文件全部内容

该命令的示例如下:

$ sh mqadmin getAccessConfigSubCommand -n 192.168.1.2:9876 -c DefaultCluster

说明:如果指定的是集群名称,则会在集群中各个broker节点执行该命令;否则会在单个broker节点执行该命令。

参数取值含义
neg:192.168.1.2:9876namesrv地址(必填)
ceg:DefaultCluster指定集群名称(与broker地址二选一)
beg:192.168.12.134:10911指定broker地址(与集群名称二选一)

 

特别注意开启Acl鉴权认证后导致Master/Slave和Dledger模式下Broker同步数据异常的问题, 在社区[4.5.1]版本中已经修复,具体的PR链接为:​​​​​​https://github.com/apache/rocketmq/pull/1149;

1 JVM选项

推荐使用最新发布的 JDK 版本。通过设置相同的 Xms 和 Xmx 值来防止 JVM 调整堆大小以获得更好的性能。生产环境 JVM 配置如下所示:

-server -Xms8g -Xmx8g -Xmn4g 

当 JVM 是默认 8 字节对齐,建议配置最大堆内存不要超过 32 G,否则会影响 JVM 的指针压缩技术,浪费内存。

如果您不关心 RocketMQ Broker 的启动时间,还有一种更好的选择,就是通过 “预触摸” Java 堆以确保在JVM初始化期间每个页面都将被分配。那些不关心启动时间的人可以启用它:

-XX:+AlwaysPreTouch  

信息

生产环境集群 Broker 一般建议配置足够的内存,避免使用小规格内存机器部署。因为Broker是重度依赖内存PageCache做性能优化的,内存过小可能造成性能不稳定。

禁用偏置锁定可能会减少 JVM 暂停:

-XX:-UseBiasedLocking   

垃圾回收,建议使用 JDK 1.8 自带的 G1 收集器:

-XX:+UseG1GC 
-XX:G1HeapRegionSize=16m   
-XX:G1ReservePercent=25 
-XX:InitiatingHeapOccupancyPercent=30

这些 GC 选项看起来有点激进,但事实证明它在我们的生产环境中具有良好的性能。

另外不要把 -XX:MaxGCPauseMillis 的值设置太小,否则 JVM 将使用一个小的年轻代来实现这个目标,这将导致非常频繁的 minor GC,所以建议使用 rolling GC 日志文件:

-XX:+UseGCLogFileRotation   
-XX:NumberOfGCLogFiles=5 
-XX:GCLogFileSize=30m

如果写入 GC 文件会增加代理的延迟,可以考虑将 GC 日志文件重定向到内存文件系统:

-Xloggc:/dev/shm/mq_gc_%p.log123   

2 Linux内核参数

os.sh 脚本在 bin 文件夹中列出了许多内核参数,可以进行微小的更改然后用于生产用途。下面的参数需要注意,更多细节请参考 /proc/sys/vm/*的 文档

  • vm.extra_free_kbytes 告诉 VM 在后台回收(kswapd)启动的阈值与直接回收(通过分配进程)的阈值之间保留额外的可用内存。RocketMQ 使用此参数来避免内存分配中的长延迟。(与具体内核版本相关)
  • vm.min_free_kbytes 如果将其设置为低于 1024 KB,将会巧妙的将系统破坏,并且系统在高负载下容易出现死锁。
  • vm.max_map_count 限制一个进程可能具有的最大内存映射区域数。RocketMQ 将使用 MMAP 加载 CommitLog 和 ConsumeQueue,因此建议将为此参数设置较大的值。
  • vm.swappiness 定义内核交换内存页面的积极程度。较高的值会增加攻击性,较低的值会减少交换量。建议将值设置为 10 来避免交换延迟。
  • File descriptor limits RocketMQ 需要为文件( CommitLog 和 ConsumeQueue )和网络连接打开文件描述符。我们建议设置文件描述符的值为 655350。
  • Disk scheduler RocketMQ建议使用I/O截止时间调度器,它试图为请求提供有保证的延迟。

 

1 正确订阅关系示例

1.1 订阅的Topic一样,且过滤表达式一致

如下图所示,同一 ConsumerGroup 下的三个Consumer实例C1、C2和C3分别都订阅了TopicA,且订阅TopicA的Tag也都是Tag1,符合订阅关系一致原则。

正确示例代码一

C1、C2、C3的订阅关系一致,即C1、C2、C3订阅消息的代码必须完全一致,代码示例如下:

        PushConsumer consumer1 = provider.newPushConsumerBuilder().setConsumerGroup("GroupA").build();
        consumer1.subscribe("TopicA", new FilterExpression("TagA", FilterExpressionType.TAG));
        
        PushConsumer consumer2 = provider.newPushConsumerBuilder().setConsumerGroup("GroupA").build();
        consumer2.subscribe("TopicA", new FilterExpression("TagA", FilterExpressionType.TAG));
        
        PushConsumer consumer3 = provider.newPushConsumerBuilder().setConsumerGroup("GroupA").build();
        consumer3.subscribe("TopicA", new FilterExpression("TagA", FilterExpressionType.TAG));

信息

RocketMQ 强调订阅关系一致,核心是指相同 ConsumerGroup 的每个 Consumer 之间一致,因为在服务端视角看来一个 Group 下的所有 Consumer 都应该是相同的副本逻辑。

强调订阅关系一致,并不是指一个 Consumer 不能订阅多个Topic,每个 Consumer 仍然可以按照需要订阅多个 Topic,但前提是相同消费者分组下的 Consumer 要一致。

2 订阅关系不一致的排查

问题描述

在使用 Apache RocketMQ 时,可能会出现订阅关系不一致的情况,具体的问题现象如下:

  • Apache RocketMQ 控制台中订阅关系是否一致显示为否。
  • 消费者(Consumer)实例未收到订阅的消息。

请参考以下步骤进行检查

您可在消息Apache RocketMQ的控制台或者CLi工具查看指定Group的订阅关系是否一致。若查询结果不一致,请参见本文(3 常见订阅关系不一致问题)排查Consumer实例的消费代码。

  1. 检查您Consumer实例中与订阅相关的配置代码,确保配置同一个 ConsumerGroup 的所有Consumer实例均订阅相同的Topic及Tag。
  2. 使用控制台或者Cli命令ConsumerConnection 查看生效的订阅关系是否一致。
  3. 测试并确认消息能够被预期的Consumer实例所消费。

3 常见订阅关系不一致问题

3.1 同一ConsumerGroup下的Consumer实例订阅的Topic不同(3.x、4.x SDK适用)

在早期3.x/4.x 版本的SDK中,如下图所示,同一 ConsumerGroup 下的三个Consumer实例C1、C2和C3分别订阅了TopicA、TopicB和TopicC,订阅的Topic不一致,不符合订阅关系一致性原则。

备注

5.x版本SDK 已经支持同一个 ConsumerGroup 下的Consumer实例订阅不同的Topic。

3.2 同一 ConsumerGroup 下的 Consumer 实例订阅的Topic相同,但订阅的Tag不一致

如下图所示,同一 ConsumerGroup 下的三个Consumer实例C1、C2和C3分别都订阅了TopicA,但是C1订阅TopicA的Tag为Tag1,C2和C3订阅的TopicA的Tag为Tag2,订阅同一Topic的Tag不一致,不符合订阅关系一致性原则。

错误示例代码二

  • Consumer实例2-1:

          PushConsumer consumer1 = provider.newPushConsumerBuilder().setConsumerGroup("GroupA").build();
          consumer1.subscribe("TopicA", new FilterExpression("Tag1", FilterExpressionType.TAG));
    

  • Consumer实例2-2:

          PushConsumer consumer2 = provider.newPushConsumerBuilder().setConsumerGroup("GroupA").build();
          consumer2.subscribe("TopicA", new FilterExpression("Tag2", FilterExpressionType.TAG));
    

  • Consumer实例2-3:

          PushConsumer consumer3 = provider.newPushConsumerBuilder().setConsumerGroup("GroupA").build();
          consumer3.subscribe("TopicA", new FilterExpression("Tag2", FilterExpressionType.TAG));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值