【kafka系列】At Least Once语义

目录

[1. At-Least-Once语义的定义](#1. At-Least-Once语义的定义)

[2. Kafka实现At-Least-Once的机制](#2. Kafka实现At-Least-Once的机制)

[2.1 生产者端](#2.1 生产者端)

[2.2 消费者端](#2.2 消费者端)

[3. At-Least-Once示例](#3. At-Least-Once示例)

场景描述

[3.1 生产者代码(可能重复发送)](#3.1 生产者代码(可能重复发送))

[3.2 消费者代码(可能重复处理)](#3.2 消费者代码(可能重复处理))

[4. 典型重复场景分析](#4. 典型重复场景分析)

场景1:生产者重试导致消息重复

场景2:消费者重复处理消息

[5. 业务端如何应对重复](#5. 业务端如何应对重复)

[5.1 幂等性处理](#5.1 幂等性处理)

[5.2 消费者端去重](#5.2 消费者端去重)

[6. At-Least-Once vs Exactly-Once](#6. At-Least-Once vs Exactly-Once)

[7. 总结](#7. 总结)


1. At-Least-Once语义的定义

At-Least-Once(至少一次) 语义指:

  • 消息从生产者到Broker:确保消息至少成功写入一次(可能因重试导致重复)。
  • 消息从Broker到消费者:确保消息至少被消费一次(可能因消费者重复拉取导致处理多次)。

核心特点消息不丢失,但可能重复


2. Kafka实现At-Least-Once的机制

2.1 生产者端
  • 配置 acks=all:确保消息被所有ISR副本写入后才返回成功。
  • 开启重试( retries > 0**)**:若Broker未及时响应,生产者自动重试发送消息。
  • 风险:若Broker已写入但未返回ACK(如网络超时),生产者重试会导致消息重复。
2.2 消费者端
  • 手动提交Offset :消费者处理完消息后手动调用commitSync()提交Offset。
  • 风险:若消费者处理消息后崩溃,未提交Offset,下次启动时会重新拉取消息,导致重复处理。

3. At-Least-Once示例

场景描述

一个用户积分系统:

  • 生产者 :发送用户积分增加消息到Topic user_points
  • 消费者 :消费消息,为用户增加积分,并提交Offset。
    要求:积分必须至少增加一次(允许重复增加,但需业务端处理重复)。

3.1 生产者代码(可能重复发送)
java 复制代码
// 生产者配置(At-Least-Once)
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092");
props.put("acks", "all");      // 确保消息写入所有ISR副本
props.put("retries", 3);       // 重试3次

KafkaProducer<String, String> producer = new KafkaProducer<>(props);

// 发送积分消息(可能重复)
producer.send(new ProducerRecord<>("user_points", "user-1001", "+10"));

潜在问题

若Broker写入成功但网络超时,生产者重试会导致消息重复发送到Topic。


3.2 消费者代码(可能重复处理)
java 复制代码
// 消费者配置(At-Least-Once)
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092");
props.put("group.id", "points-group");
props.put("enable.auto.commit", "false"); // 关闭自动提交

KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("user_points"));

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        // 处理消息:为用户增加积分
        addUserPoints(record.key(), Integer.parseInt(record.value()));
        // 手动提交Offset(可能失败)
        consumer.commitSync();
    }
}

潜在问题

addUserPoints()执行成功但commitSync()失败(如消费者崩溃),下次启动时会重新拉取并处理同一消息,导致积分重复增加。


4. 典型重复场景分析

场景1:生产者重试导致消息重复
  • 原因:Broker已写入消息但ACK丢失,生产者重试发送。
  • 结果 :Topic中存在两条相同消息(如user-1001, +10)。
场景2:消费者重复处理消息
  • 原因:消费者处理消息后未提交Offset(如崩溃)。
  • 结果:重启后重新拉取并处理同一消息,积分被重复增加。

5. 业务端如何应对重复

5.1 幂等性处理
  • 数据库唯一约束:为每条消息生成唯一ID,插入时去重。
sql 复制代码
INSERT INTO user_points (user_id, points, msg_id) 
VALUES ('user-1001', 10, 'uuid-123') 
ON CONFLICT (msg_id) DO NOTHING;
  • 业务逻辑幂等:设计接口支持多次调用结果一致。
java 复制代码
// 幂等积分增加方法
public void addPoints(String userId, int points, String msgId) {
    if (!isMessageProcessed(msgId)) {
        updateUserPoints(userId, points);
        markMessageAsProcessed(msgId);
    }
}
5.2 消费者端去重
  • 本地记录已处理消息:使用缓存或数据库记录已处理的Offset或消息ID。
java 复制代码
// 消费者处理逻辑(伪代码)
for (ConsumerRecord record : records) {
    if (!processedMessages.contains(record.offset())) {
        addUserPoints(record.key(), record.value());
        processedMessages.add(record.offset());
        consumer.commitSync();
    }
}

6. At-Least-Once vs Exactly-Once

|--------|-------------------|-----------------------|
| 特性 | At-Least-Once | Exactly-Once |
| 消息丢失风险 | 不丢失 | 不丢失 |
| 消息重复风险 | 可能重复 | 不重复 |
| 性能开销 | 低(无需事务) | 高(事务与协调开销) |
| 适用场景 | 允许重复的业务(如日志采集) | 金融交易、精准统计 |
| 实现复杂度 | 简单 | 复杂(需生产者、Broker、消费者协同) |


7. 总结

  • At-Least-Once是Kafka的默认语义 :通过acks=all和手动提交Offset实现,简单高效。
  • 业务端必须处理重复:通过幂等性设计或去重机制避免数据不一致。
  • 适用场景:日志采集、指标上报等允许少量重复但对丢失敏感的场景。

通过合理配置和业务设计,At-Least-Once可平衡可靠性与性能,是大多数场景的推荐选择。

相关推荐
盖世英雄酱581366 分钟前
🚀不改SQL,也能让SQL的执行效率提升100倍
java·数据库·后端
Xy91018 分钟前
从代码角度拆解Apptrace的一键拉起
javascript·数据库
袋鼠云数栈2 小时前
国产数据库StarRocks在数栈轻量化数据开发的全流程实践
数据库
Listennnn2 小时前
Text2SQL、Text2API基础
数据库·人工智能
夜光小兔纸2 小时前
SQL Server 修改数据库名及物理数据文件名
运维·数据库·sql server
元气满满的热码式2 小时前
Rabbitmq后台无法登录问题解决
分布式·rabbitmq·ruby
A尘埃2 小时前
RabbitMQ可靠和延迟队列
分布式·rabbitmq
····懂···3 小时前
数据库专家 OCP 认证培训:开启职业黄金赛道
数据库·oracle·ocp
天天摸鱼的java工程师3 小时前
Kafka是如何保证消息队列中的消息不丢失、不重复?
java·后端·kafka
明月看潮生3 小时前
青少年编程与数学 01-011 系统软件简介 18 Navicat数据库管理工具
数据库·青少年编程·系统软件·编程与数学