Skip to content

生产者与消费者

1. 这是什么

生产者负责发送消息,消费者负责拉取和处理消息。
这是 Kafka 业务接入层最直接的两个角色。

一句话理解:

  • Producer 决定“消息怎么发进去”
  • Consumer 决定“消息怎么读出来并处理完”

2. 为什么重要

大多数消息链路问题,都发生在这几个环节:

  • 生产发送
  • 消费处理
  • offset 提交
  • 再均衡

理解两端行为,是真正用好 Kafka 的基础。

3. 先建立直觉:发送成功不等于业务完成

一个非常常见的误区是:

  • Producer 发送成功了,链路就万事大吉

其实远远不是。
真正的业务完成,还取决于:

  • 消费者是否真的处理成功
  • offset 是否按预期提交
  • 失败后是否会重复处理或漏处理

4. 核心内容

4.1 生产发送流程

生产者的核心工作不只是“发消息”,还包括:

  • 序列化
  • 选择分区
  • 发送确认
  • 重试控制

4.2 分区选择策略

生产者通常需要决定:

  • 消息进入哪个分区

这通常和:

  • key
  • 分区策略

密切相关。

4.3 消费拉取流程

消费者的核心流程通常是:

  1. 拉取消息
  2. 执行业务处理
  3. 提交 offset

这三步的顺序和策略,会直接影响:

  • 是否重复消费
  • 是否漏消费

4.4 自动提交和手动提交

自动提交的优点是:

  • 简单

但风险是:

  • 业务还没处理成功,offset 可能已经提交

手动提交更灵活,因为你可以在:

  • 真正处理成功后再提交

4.5 消费组再均衡

当这些情况发生时,消费组通常会再均衡:

  • 新消费者加入
  • 消费者下线
  • 分区数变化

再均衡的代价是:

  • 分区归属变动
  • 短时间消费抖动

5. 学习重点

这一章最重要的是掌握:

  • 发送成功不等于业务处理完成
  • 自动提交和手动提交 offset 的语义差别很大
  • 再均衡会影响消费稳定性
  • 生产端和消费端要分别考虑可靠性

6. 常见问题

6.1 只关注发消息,不关注消费端语义

这会让消息链路看起来“很通”,但业务语义并不稳。

6.2 自动提交导致消息处理不一致

这是非常高频的消费语义坑。

6.3 消费组变动时忽视再均衡成本

消费并发扩展不是没有代价的。

7. 动手验证

当前环境没有 Kafka CLI,这里用纯 Java demo 直接验证“自动提交”和“手动提交”两种心智差异。

7.1 准备一个可运行示例

新建文件 KafkaConsumerSemanticsDemo.java,内容如下:

java
import java.util.ArrayList;
import java.util.List;

public class KafkaConsumerSemanticsDemo {
    static class Record {
        final long offset;
        final String value;

        Record(long offset, String value) {
            this.offset = offset;
            this.value = value;
        }
    }

    public static void main(String[] args) {
        List<Record> partition = new ArrayList<>();
        partition.add(new Record(0, "msg-0"));
        partition.add(new Record(1, "msg-1"));

        long autoCommittedOffset = 0;
        try {
            Record r = partition.get(0);
            autoCommittedOffset = r.offset + 1; // 先提交
            throw new RuntimeException("business fail");
        } catch (Exception e) {
            System.out.println("autoCommitLostRisk=true");
            System.out.println("autoCommittedOffset=" + autoCommittedOffset);
        }

        long manualCommittedOffset = 0;
        try {
            Record r = partition.get(0);
            throw new RuntimeException("business fail");
        } catch (Exception e) {
            System.out.println("manualCommitRetryPossible=true");
            System.out.println("manualCommittedOffset=" + manualCommittedOffset);
        }

        manualCommittedOffset = 1;
        System.out.println("manualCommittedAfterSuccess=" + manualCommittedOffset);
    }
}

7.2 编译并运行

bash
javac KafkaConsumerSemanticsDemo.java
java KafkaConsumerSemanticsDemo

7.3 你应该观察到什么

text
autoCommitLostRisk=true
autoCommittedOffset=1
manualCommitRetryPossible=true
manualCommittedOffset=0
manualCommittedAfterSuccess=1

7.4 每一行在验证什么

  • autoCommittedOffset=1:说明自动提交可能在业务失败前就推进 offset
  • manualCommittedOffset=0:说明手动提交让失败消息仍然有重试机会
  • manualCommittedAfterSuccess=1:说明处理成功后再提交 offset,语义更可控

8. 练习建议

  • 写一个简单生产消费 demo
  • 对比自动提交和手动提交 offset
  • 记录一次消费组再均衡过程
  • 总结“发送成功”和“处理成功”的区别

9. 自测问题

  • 自动提交 offset 有什么风险?
  • 再均衡为什么会影响消费稳定性?
  • 生产端和消费端应该分别关注哪些可靠性问题?
  • 为什么说发送成功不等于业务完成?

10. 自测核对要点

  • Producer 成功发送只是消息进入链路的一部分
  • Consumer 的处理和 offset 提交顺序非常关键
  • 自动提交简单,但语义风险更高
  • 再均衡会带来消费抖动和分区重分配