Kafka 的并发模型就两条规则:一个分区只能被同组的一个消费者消费;不同组之间互不影响。把这两条吃透,很多问题就解释得通了。
分区是并行的单位
一个 topic 分成多个 partition,每个 partition 内部有序,partition 之间无序。消费者数量超过分区数时,多的消费者会空闲——因为没有分区可以分给它。
所以“消费者加到 8 个还是不提速”,先看 topic 有几个分区。3 个分区最多 3 个消费者并行,剩下的都在等。
分区数怎么定
- 目标吞吐 / 单消费者吞吐 = 最小分区数
- 分区数只能增不能减,增加会导致 key 到分区的映射变化,顺序性被破坏
- 分区太多会增加 Broker 的文件句柄和元数据开销,单 topic 几百个分区就要留意了
经验值:中小规模 6~12 个分区,够用且好管理。
消息怎么分配到分区
// 指定 key:同一 key 一定进同一分区,保证同订单有序
producer.send(new ProducerRecord<>("orders", orderId, payload));
// 不指定 key:粘性分区,一批一批轮询,吞吐高但无序
producer.send(new ProducerRecord<>("orders", payload));
只要业务要求“同一实体的事件有序”,就必须用实体 id 做 key。用随机分区再指望消费端排序,是自找麻烦。
消费者组与 rebalance
消费者加入或离开组,会触发 rebalance:分区重新分配,期间所有消费者暂停消费。频繁 rebalance 是生产环境的常见问题。
触发原因:
- 消费者处理太慢,超过
max.poll.interval.ms(默认 5 分钟),被踢出组 - 心跳线程没发心跳,超过
session.timeout.ms - 消费者重启、扩容
对策:
- 调大
max.poll.interval.ms,或者减小max.poll.records,让单次处理时间变短 - 用
CooperativeStickyAssignor增量式 rebalance,只迁移必要的分区 - 静态成员
group.instance.id,重启时不触发 rebalance
offset 提交时机
自动提交(enable.auto.commit=true)默认每 5 秒一次,可能在处理失败前就提交了 offset,导致消息丢失。手动提交更安全:
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(500));
for (ConsumerRecord<String, String> r : records) {
process(r);
}
consumer.commitSync();
}
批量提交有重复消费的风险(处理完了没提交就崩),所以消费逻辑要幂等。
一句话总结
分区决定并行度,key 决定顺序,消费者组决定隔离。设计的时候先想清楚“哪些消息必须有序”,剩下的才好安排。
- THE END -
最后修改:2023年7月8日
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/archives/673
共有 0 条评论