生产者最佳实践

#RocketMQ #消息队列 #生产者 #最佳实践

总结
  • 一个应用一个 Topic,消息子类型用 Tag 区分
  • 业务唯一标识写到 keys,方便排查消息丢失
  • 发送结果除了 SEND_OK,其他状态都要警惕,可能丢消息

1. Topic 怎么设计

一个应用尽量只用一个 Topic,消息子类型用 Tag 来区分。

比如订单系统:

Topic: ORDER_TOPIC
├── Tag: CREATE     → 创建订单
├── Tag: PAY        → 支付成功
└── Tag: CANCEL     → 取消订单

好处:代码里只维护一个 Topic,通过 Tag 过滤,结构清晰。

消费者订阅时可以用 Tag 过滤,Broker 端直接过滤,不用把所有消息都拉下来再筛:

// 订阅时指定 Tag
consumer.subscribe("ORDER_TOPIC", "PAY || CANCEL");

2. Key 怎么用

每个消息都要设 keys,放业务层面的唯一标识,比如订单 ID。

// 设置业务唯一标识
String orderId = "20034568923546";
message.setKeys(orderId);

为什么要设 Key?

Key 要尽量唯一,避免哈希冲突。如果多个消息用同一个 Key,查出来一堆,不好定位。

3. 日志怎么打

消息发送成功或失败,都要打印日志。必须包含这两个字段

SendResult sendResult = producer.send(message);
log.info("发送结果: {}, key: {}", sendResult.getSendStatus(), message.getKeys());

不打日志的话,出了问题查都没法查。

5. 相关内容

发送是消息流程的起点,消费是终点,详见 RocketMQ中基本概念 中的消费者部分。如果你在面试中被问到 RocketMQ 的发送机制,可以重点准备 RocketMQ面试题 中的生产者实践相关问题。

6. 发送结果怎么看

调用 send 方法不抛异常就代表发送成功,但成功有不同状态:

状态 含义 会不会丢消息
SEND_OK 发送成功,已刷盘或同步到 Slave 不会
FLUSH_DISK_TIMEOUT 发送成功,但刷盘超时 Broker 挂了会丢
FLUSH_SLAVE_TIMEOUT 发送成功,但同步到 Slave 超时 Broker 挂了会丢
SLAVE_NOT_AVAILABLE 发送成功,但 Slave 不可用 Broker 挂了会丢
graph TD
    A["send() 调用"] --> B{"有异常?"}
    B -->|有| C["发送失败,需要重试"]
    B -->|无| D{"状态是?"}
    D -->|"SEND_OK"| E["安全,不会丢"]
    D -->|"FLUSH_DISK_TIMEOUT"| F["有风险,Broker挂了会丢"]
    D -->|"FLUSH_SLAVE_TIMEOUT"| F
    D -->|"SLAVE_NOT_AVAILABLE"| F

怎么处理?