为什么高吞吐选 Kafka
系统解耦、异步削峰、日志采集、实时埋点,这些场景都离不开消息队列。RabbitMQ 功能全、路由灵活,但单机吞吐有限;Kafka 用“把消息追加到磁盘顺序日志”的方式,把高吞吐做到了百万级每秒,非常适合日志、行为、订单这类大数据量场景。
和 RabbitMQ 的“消息被消费完就删”不同,Kafka 把消息按分区持久化保存一段时间,消费位置靠 offset 记录。这意味着同一条消息可以被多个消费组独立消费、可以回溯重放,这正是它适合做数据管道和流处理的原因。
- 分区topic 拆成多个 partition,是并行与扩展的基础。
- 消费组同一组内分区被分着消费,不同组各收全量。
- offset消费位点,决定从哪继续读。
- 副本分区有 leader 和 follower,保证宕机不丢。
topic、分区与消费组
几个名词先理清:topic 是一类消息的主题(如“订单事件”);一个 topic 拆成多个 partition(分区),分布在不同 broker 上实现并行;每条消息进分区后按顺序追加,带一个递增编号叫 offset。
消费组(consumer group)是 Kafka 最巧妙的设计:同一个消费组里,各分区只会被组内一个消费者消费,从而横向扩容;而不同的消费组彼此独立,每个组都能收到全量消息。于是“订单服务”和“数据分析”可以挂在同一 topic 上各取所需,互不影响。
生产者与消费者代码
生产者把消息发到 topic,可按 key 决定进哪个分区(相同 key 进同一分区,保证有序):
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "StringSerializer");
props.put("value.serializer", "StringSerializer");
Producer<String,String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("order-events", "order-1001", "paid"));
// 消费者:指定消费组,拉取并处理
props.put("group.id", "order-service");
props.put("auto.offset.reset", "earliest");
consumer.subscribe(Arrays.asList("order-events"));
while (true) {
ConsumerRecords<String,String> rs = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String,String> r : rs) { handle(r); }
}
消费完是否自动提交 offset,要根据业务定:自动提交简单但可能“处理失败却已提交”,生产上常用手动提交,处理成功再提交位点。
分区数与并行度
分区数直接决定这个 topic 最多能并行多少消费者:一个分区同一时刻只能被消费组内一个线程消费,所以消费并行度上限就是分区数。想让消费扩容,就得加分区。
但分区不是越多越好:分区越多,broker 上文件句柄和副本开销越大。经验上按预期峰值吞吐预估,并给未来留余量——注意分区数只能加、不能随意减少,建 topic 时就要想好。
投递语义与重试
Kafka 提供三种投递语义:至多一次(可能丢)、至少一次(可能重复、不丢)、恰好一次(不丢不重,配置复杂)。大多数业务选“至少一次”,然后让消费端幂等——同一消息处理两遍结果一样,重复就无害了。
消费失败别直接丢:记日志、重试几次,仍失败就写进死信队列(DLQ),人工或后续补偿处理。配合退避重试,避免一条脏消息把整个分区卡死。
集群、副本与高可用
Kafka 集群由多台 broker 组成,每个分区有一个 leader(负责读写)和若干 follower(同步副本)。leader 宕机时,控制器从 follower 里选一个新 leader,业务几乎无感。副本因子通常配 3,保证一台机器挂了数据还在。
生产者这边用 acks 参数控制“写成功”的严格程度:acks=all 表示等所有同步副本都写好才算成功,吞吐略降但最可靠,关键事件(如付款)建议用它。
积压与消费失败治理
线上最常见的问题是消费积压:生产速度大于消费速度,lag(未消费消息数)持续上涨。排查顺序:先看是不是某条消息反复失败卡住分区,再看消费线程数够不够、下游(数据库/接口)是不是变慢拖慢了整体。临时手段是扩容消费者(前提是分区数够)、或批量处理,但根因往往在下游。
消费失败要分类:可重试的网络错就重试并退避;业务数据本身有问题就直接送 DLQ,别阻塞分区。监控上要盯 lag、消费速率、失败率这几个指标,参考本站《后端可观测性实战》把它们接进告警。
实战清单
落地清单:
- 大数据量、可回溯的场景优先 Kafka,路由复杂再考虑 RabbitMQ。
- 消费并行度上限等于分区数,扩容前先加分区。
- 消费端做幂等,配合死信队列兜底失败消息。
- 关键事件用 acks=all、副本因子 3,保证不丢。
- 盯紧 lag、失败率,积压先查下游和脏消息。
- 同 topic 挂多消费组,让不同业务各取所需。
Kafka 的价值在于把“高吞吐、可持久、可回放”的消息管道做成基础设施。把分区、消费组、offset 这三件事理解透,异步削峰和数据管道就有了可靠的底座。