Files

5.0 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
backend.mq.kafka Kafka:主题、分区、消费组、位移和顺序 lesson backend
mq
Kafka
分区
消费组
offset
顺序消息
ISR
intermediate
backend.mq.rabbitmq
3
backend.mq.reliability
backend.mq.interview
14
2

Kafka:主题、分区、消费组、位移和顺序

解释说明

模型:Kafka 是一个分布式的追加日志。消息写入主题(Topic),主题分成多个分区(Partition),每个分区是一个只追加的有序日志,消息有递增的位移(offset)。消息被消费后不会删除,按保留时间或大小清理,所以可以回溯重新消费。

  • 生产者:消息带 key 时,同一个 key 总是进入同一个分区(按 key 哈希);没有 key 时分散写入。
  • 消费组(Consumer Group):同一个组里,一个分区同一时间只分配给一个消费者;不同组各自独立消费全部消息。所以消费者数量超过分区数时,多出来的消费者会空闲——分区数决定了组内并行度上限。
  • 位移提交:消费者记录“处理到哪里了”。先处理再提交 = 至少一次(可能重复);先提交再处理 = 至多一次(可能丢)。业务一般选至少一次 + 幂等消费。
  • 副本:每个分区有多个副本,一个 leader 负责读写,follower 同步。和 leader 保持同步的副本集合叫 ISR。
  • 生产者 acks:acks=0 不等确认;acks=1 leader 写入即确认;acks=all ISR 都写入才确认(配合 min.insync.replicas 保证不丢)。
  • 顺序:只保证单个分区内有序。需要“同一订单的消息有序”时,用订单 ID 作为 key。

和 RabbitMQ 怎么选

RabbitMQ Kafka
模型 消息被消费确认后删除 日志保留,可回溯
优势 路由灵活、延迟 / 死信 / 优先级等功能多、单条消息时延低 吞吐量高、可回放、适合流处理
典型场景 业务异步任务、订单流程 日志采集、埋点、数据同步、事件流

新版本 Kafka 已用 KRaft 取代 ZooKeeper 管理元数据(Kafka 4.0 起移除 ZooKeeper),部署细节以官方文档为准:https://kafka.apache.org/documentation/

案例

  • 埋点数据:前端事件写入 events 主题(几十个分区),实时计算和离线入库两个消费组各自消费。
  • 订单状态变化事件:用订单 ID 作为 key,保证同一订单的“创建 → 支付 → 发货”按顺序被消费。
  • 消费者 bug 导致数据处理错误:修复后把消费组位移重置到出问题前的时间点,重新消费。

代码示例

Python 客户端(confluent-kafka,示意代码,需要运行中的 Kafka;客户端参数以官方文档为准:https://docs.confluent.io/kafka-clients/python/current/overview.html ):

import json

from confluent_kafka import Consumer, Producer

producer = Producer({"bootstrap.servers": "localhost:9092", "acks": "all", "enable.idempotence": True})


def on_delivery(err, msg):
    if err is not None:
        print("发送失败:", err)  # 这里要记录并重试或告警


for status in ("created", "paid", "shipped"):
    # 同一订单用同一个 key → 进入同一分区 → 有序
    producer.produce("order-events", key="order-1001", value=json.dumps({"status": status}), callback=on_delivery)
producer.flush()

consumer = Consumer({
    "bootstrap.servers": "localhost:9092",
    "group.id": "points-service",
    "auto.offset.reset": "earliest",
    "enable.auto.commit": False,  # 处理成功后手动提交,实现“至少一次”
})
consumer.subscribe(["order-events"])
try:
    while True:
        msg = consumer.poll(1.0)
        if msg is None:
            continue
        if msg.error():
            print("消费错误:", msg.error())
            continue
        event = json.loads(msg.value())
        print(msg.key(), msg.partition(), msg.offset(), event)  # 业务处理要幂等
        consumer.commit(message=msg, asynchronous=False)
finally:
    consumer.close()

易错点

  • 消费者比分区多:多出来的消费者空闲,加机器也提不了速度;要先加分区(分区只能增加不能减少,增加后 key 的分布会变,影响顺序)。
  • 自动提交位移:消息还没处理完就提交,崩溃后丢消息;或者处理完还没提交就崩溃,重复消费。业务里建议手动提交 + 幂等。
  • 以为整个主题有序:只有分区内有序。
  • 单条消息处理太慢:超过 max.poll.interval.ms 会被踢出消费组并触发再均衡,导致重复消费。
  • acks=1 又要求不丢:leader 宕机且 follower 未同步时会丢;要 acks=all + min.insync.replicas>=2 + 副本数 >=3。

面试常问

见消息队列面试题:Kafka 为什么吞吐高、分区和消费组的关系、怎么保证顺序、怎么不丢消息、和 RabbitMQ 的区别。