Java 消费者骨架
Java 消费者的基本结构是一个永不退出的 poll 循环:每一轮 poll 拉回一批记录,处理完再提交位移。位移提交时机直接决定语义——先提交后处理会丢消息,先处理后提交会重复消费,所以骨架里通常关闭自动提交、改成处理成功后手动提交。另外,两次 poll 之间的时间必须小于 max.poll.interval.ms,否则消费者会被踢出组并触发再均衡。
基本信息
| 订阅方式 | subscribe 走组协调;assign 手动指定分区 |
|---|---|
| 位移提交 | 处理成功后 commitSync/commitAsync |
| 退出方式 | wakeup() 打断阻塞的 poll 后 close() |
| 关键配置 | group.id、key/value.deserializer、enable.auto.commit |
| 并行度上限 | 组内消费者数超过分区数时多余的实例空转 |
| 线程模型 | Consumer 非线程安全,一个实例只在一个线程里使用 |
最小示例
最小示例
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092");
props.put("group.id", "order-consumer");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("enable.auto.commit", "false");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("orders"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> r : records) {
process(r);
}
consumer.commitSync(); // 处理完成后再提交,失败可重放
}典型使用场景
- 事件驱动业务:消费订单或支付事件触发下游动作
- 数据入仓:把消息批量写入数据库或对象存储
- 多实例并行:靠分区数决定并行上限,扩容前先确认分区是否够用
- 需要至少一次语义:处理成功再提交,下游接口做幂等
- 暂停与恢复:临时下游故障时 pause 分区,恢复后 resume 继续
关键配置读法
| 配置 | 作用 | 注意 |
|---|---|---|
| enable.auto.commit | 是否自动提交位移 | 关闭后需自行控制提交时机 |
| auto.offset.reset | 无位点或位点失效时的起始位置 | 一般用 earliest 或 latest |
| max.poll.records | 单次拉取条数 | 直接决定一轮处理时长 |
| max.poll.interval.ms | 两次 poll 的最大间隔 | 超时会被踢出组 |
| fetch.min.bytes | 单次拉取的最小数据量 | 调大可提高吞吐但增加延迟 |
poll 循环里做同步外部调用(长 SQL、慢接口)会阻塞心跳之外的处理进度,容易触发再均衡。
位移提交策略决定了故障时的行为:处理成功后提交是至少一次(可能重复),先提交再处理是至多一次(可能丢),需要精确一次时通常要配合下游幂等或事务。