RocketMQ-最佳实践

本文详细介绍了RocketMQ的Producer和Consumer的最佳实践,包括如何设置Topic和Tags,消息Key的使用,消息日志记录,重发机制,以及Consumer的幂等性、批量消费、消费并行度优化等。同时提到了其他关键配置,如关闭自动创建Topic,调整JVM和Linux内核参数,以提升RocketMQ的性能和稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址 mp.weixin.qq.com

RocketMQ 是阿里开源的消息队列框架,如今也已成为 Apache 顶级项目,RockerMQ 是一个非常优秀的框架,现在大部分互联网公司使用的消息队列也是 RocketMQ,在我们使用的过程中,如果能一开始就给你最佳实践,可以避免走一些弯路,甚至你看完之后可以自身检查下你们是不是这样使用,没有的话可以进行适当的调整,这篇文章应该能够帮助你更好的使用 RockerMQ。

1

〓Producer 最佳实践


1、Topic

一个应用尽可能用一个 Topic,消息子类型用 tags 来标识,tags 可以由应用自由设置。只有发送消息设置了 tags,消费方在订阅消息时,才可以利用 tags 在 broker 做消息过滤:message.setTags(“TagA”)。

2、Key

每个消息在业务层面的唯一标识码,要设置到 keys 字段,方便将来定位消息丢失问题。服务器会为每个消息创建索引(哈希索引),应用可以通过 topic、key 来查询这条消息内容,以及消息被谁消费。

每个消息在业务层面的唯一标识码要设置到 keys 字段,方便将来定位消息丢失问题。

由于是哈希索引,请务必保证 key 尽可能唯一,这样可以避免潜在的哈希冲突。

// 订单Id     
String orderId = "20034568923546";     
message.setKeys(orderId);

3、日志

消息发送成功或者失败,要打印消息日志,务必要打印 sendresult 和 key 字段。

send 消息方法只要不抛异常,就代表发送成功。

发送成功会有多个状态,在 sendResult 里定义。

4、重发

对于消息不可丢失应用,务必要有消息重发机制。

例如:消息发送失败,存储到数据库,能有定时程序尝试重发或者人工触发重发。

5、sendOneWay

某些应用如果不关注消息是否发送成功,请直接使用 sendOneWay 方法发送消息。

对可靠性要求并不高,例如日志收集类应用,此类应用可以采用 oneway 形式调用。

1

〓Consumer 最佳实践


1、幂等

消费过程要做到幂等(即消费端去重)。

RocketMQ 目前无法避免消息重复,所以如果业务对消费重复非常敏感,务必要在业务层面去重,有以下几种去重方式:

a). 将消息的唯一键,可以是 msgId,也可以是消息内容中的唯一标识字段,例如订单 Id 等,消费之前判断是否在 Db 或 Tair(全局 KV 存储) 中存在,如果不存在则插入,并消费,否则跳过。

b). 用业务层面的状态机去重。

2、批量消费

尽量使用批量方式消费方式,可以很大程度上提高消费吞吐量。

某些业务流程如果支持批量方式消费,则可以很大程度上提高消费吞吐量,例如订单扣款类应用,一次处理一个订单耗时 1 s,一次处理 10 个订单可能也只耗时 2 s,这样即可大幅度提高消费的吞吐量,通过设置 consumer 的 consumeMessageBatchMaxSize 返个参数,默认是 1,即一次只消费一条消息,例如设置为 N,那么每次消费的消息数小于等于 N。

3、 跳过非重要消息

发生消息堆积时,如果消费速度一直追不上发送速度,如果业务对数据要求不高的话,可以选择丢弃不重要的消息。

例如,当某个队列的消息数堆积到 100000 条以上,则尝试丢弃部分或全部消息,这样就可以快速追上发送消息的速度。

示例代码如下:

public ConsumeConcurrentlyStatus consumeMessage(

            List<MessageExt> msgs,

            ConsumeConcurrentlyContext context) {

        long offset = msgs.get(0).getQueueOffset();

        String maxOffset =

                msgs.get(0).getProperty(Message.PROPERTY_MAX_OFFSET);

        long diff = Long.parseLong(maxOffset) - offset;

        if (diff > 100000) {

            // TODO 消息堆积情况的特殊处理

            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

        }

        // TODO 正常消费过程

        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

    }

4、提高消费并行度

a). 同一个 ConsumerGroup 下,通过增加 Consumer 实例数量来提高并行度,超过订阅队列数的 Consumer 实例无效。

可以通过加机器,或者在已有机器启动多个进程的方式。

b). 提高单个 Consumer 的消费并行线程,通过修改以下参数:

consumeThreadMin consumeThreadMax

5、优化每条消息消费过程

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

  • 根据消息从 DB 查询【数据 1】

  • 根据消息从 DB 查询【数据 2】

  • 复杂的业务计算

  • 向 DB 插入【数据 3】

  • 向 DB 插入【数据 4】

这条消息的消费过程中有 4 次与 DB 的 交互,如果按照每次 5ms 计算,那么总共耗时 20ms,假设业务计算耗时 5ms,那么总过耗时 25ms,所以如果能把 4 次 DB 交互优化为 2 次,那么总耗时就可以优化到 15ms,即总体性能提高了 40%。

所以应用如果对时延敏感的话,可以把 DB 部署在 SSD 硬盘,相比于 SCSI 磁盘,前者的 RT 会小很多。

6、日志

消费时记录日志,以便后续定位问题。

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

   public ConsumeConcurrentlyStatus consumeMessage(

            List<MessageExt> msgs,

            ConsumeConcurrentlyContext context) {

        log.info("RECEIVE_MSG_BEGIN: " + msgs.toString());

        // TODO 正常消费过程

        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

    }   

如果能打印每条消息消费耗时,那么在排查消费慢等线上问题时,会更方便。

〓其他配置


1、线上应该关闭 autoCreateTopicEnable,即在配置文件中将其设置为 false。

RocketMQ 在发送消息时,会首先获取路由信息。如果是新的消息,由于 MQServer 上面还没有创建对应的 Topic,这个时候,如果上面的配置打开的话,会返回默认 TOPIC 的(RocketMQ 会在每台 broker 上面创建名为 TBW102 的 TOPIC)路由信息,然后 Producer 会选择一台 Broker 发送消息,选中的 broker 在存储消息时,发现消息的 topic 还没有创建,就会自动创建 topic。

后果就是:以后所有该 TOPIC 的消息,都将发送到这台 broker 上,达不到负载均衡的目的。

所以基于目前 RocketMQ 的设计,建议关闭自动创建 TOPIC 的功能,然后根据消息量的大小,手动创建 TOPIC。

2、JVM 选项

推荐使用最新发布的 JDK 1.8 版本。通过设置相同的 Xms 和 Xmx 值来防止 JVM 调整堆大小以获得更好的性能。

简单的 JVM 配置如下所示:

-server -Xms8g -Xmx8g -Xmn4g

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

那些不关心启动时间的人可以启用它: -XX:+AlwaysPreTouch

禁用偏置锁定可能会减少 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

3、Linux 内核参数

os.sh 脚本在 bin 文件夹中列出了许多内核参数,可以进行微小的更改然后用于生产用途。

下面的参数需要注意,更多细节请参考 /proc/sys/vm/* 的文档

  • vm.extra_free_kbytes,告诉 VM 在后台回收(kswapd)启动的阈值与直接回收(通过分配进程)的阈值之间保留额外的可用内存。RocketMQ 使用此参数来避免内存分配中的长延迟。(与具体内核版本相关)

  • vm.min_free_kbytes,如果将其设置为低于 1024KB,将会巧妙的将系统破坏,并且系统在高负载下容易出现死锁。

  • vm.max_map_count,限制一个进程可能具有的最大内存映射区域数。RocketMQ 将使用 mmap 加载 CommitLog 和 ConsumeQueue,因此建议将为此参数设置较大的值。(agressiveness --> aggressiveness)

  • vm.swappiness,定义内核交换内存页面的积极程度。较高的值会增加攻击性,较低的值会减少交换量。建议将值设置为 10 来避免交换延迟。

  • File descriptor limits,RocketMQ 需要为文件(CommitLog 和 ConsumeQueue)和网络连接打开文件描述符。我们建议设置文件描述符的值为 655350。

  • Disk scheduler,RocketMQ 建议使用 I/O 截止时间调度器,它试图为请求提供有保证的延迟。

    如果您有更好的实践经验,欢迎留言。

▽参考资料:

http://jm.taobao.org/2017/03/09/20170309/

https://github.com/apache/rocketmq/blob/master/docs/cn/best_practice.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值