Java ApacheKafka-出现错误时是否可能丢失消息?
我正在深入研究并观察一些行为,这些行为让我怀疑我是否做错了什么,或者它是否按预期工作——我几乎不怀疑: 出错时可能会丢失消息 我的设置尽可能简单。一个Kafka代理和一个只有1个分区的主题。具有默认设置的代理、主题、生产者和消费者(自动确认为true) 测试用例1Java ApacheKafka-出现错误时是否可能丢失消息?,java,apache-kafka,spring-cloud-stream,Java,Apache Kafka,Spring Cloud Stream,我正在深入研究并观察一些行为,这些行为让我怀疑我是否做错了什么,或者它是否按预期工作——我几乎不怀疑: 出错时可能会丢失消息 我的设置尽可能简单。一个Kafka代理和一个只有1个分区的主题。具有默认设置的代理、主题、生产者和消费者(自动确认为true) 测试用例1 生成message1 生成message2 启动一个使用者,该使用者在接收任何消息时抛出RuntimeException 正在使用消息1,请重试 正在使用消息1,请重试 正在使用消息1,请重试 异常被抛出 正在使用消息2,请重试 正
- 生成
message1
- 生成
message2
- 启动一个使用者,该使用者在接收任何消息时抛出RuntimeException
- 正在使用
,请重试消息1
- 正在使用
,请重试消息1
- 正在使用
,请重试消息1
- 异常被抛出
- 正在使用
,请重试消息2
- 正在使用
,请重试消息2
- 正在使用
,请重试消息2
- 异常被抛出
- 停止并重新启动消费者
- 正在使用
,请重试消息1
- 正在使用
,请重试消息1
- 正在使用
,请重试消息1
- 异常被抛出
- 正在使用
,请重试消息2
- 正在使用
,请重试消息2
- 正在使用
,请重试消息2
- 异常被抛出
- 生成
message1
- 生成
message2
- 启动一个使用者,该使用者在接收到确切的
消息1
- 正在使用
,请重试消息1
- 正在使用
,请重试消息1
- 正在使用
,请重试消息1
- 异常被抛出
- 已成功使用
message2
- 生成
message3
- 已成功使用
message3
- 停止并重新启动消费者
- 什么也没有发生,消费者等待新的消息来消费
message1
将被跳过,因为提交的偏移量已设置为message3
。这就是困扰我的。我不希望消费者继续发送消息,只要之前的消息未成功处理
有没有人经历过同样的行为和/或可以指导我如何改变这种情况
提前谢谢
更新:根据要求,更新一些代码片段 创建主题
kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test-topic
联系制作人
kafka-console-producer.sh --broker-list localhost:9092 --topic test-topic
使用创建maven项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
<relativePath/>
</parent>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
</dependencies>
添加以下Application.java
@SpringBootApplication
@EnableBinding(Sink.class)
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@StreamListener(Sink.INPUT)
private void consume(Message<String> message) {
log.info("Received: {}", message.getPayload());
if ("message1".equals(message.getPayload())
throw new RuntimeException();
log.info("Successfully processed message {}", message.getPayload());
}
}
@springboot应用程序
@EnableBinding(Sink.class)
公共类应用程序{
私有静态最终记录器log=LoggerFactory.getLogger(Application.class);
公共静态void main(字符串[]args){
SpringApplication.run(Application.class,args);
}
@StreamListener(Sink.INPUT)
专用void消费(消息){
log.info(“接收:{}”,message.getPayload());
if(“message1”.equals(message.getPayload())
抛出新的RuntimeException();
log.info(“已成功处理消息{}”,message.getPayload());
}
}
应该是这样。运行应用程序并使用控制台生成器生成消息。Kafka为您提供了一个运行时,但您可以选择。在某些情况下,MSG可能会丢失/跳过,在某些情况下可能不会丢失/跳过-您需要根据需要准备配置。在我看来,您应该进一步研究Spring Cloud Stream se的一些特性ttings。您还可以“手动”禁用自动提交和提交偏移量.在Kafka中,每条消息都带有一个偏移id。您的使用者应用程序可以对偏移进行检查,如果有任何偏移被跳过或丢失,则不使用下一条消息。您可以使用使用者。seek方法获取丢失的特定消息 偏移量本质上是递增的,并且是连续的 在您的情况下,使用手动提交 我可以说使用以下步骤
请参阅您应该为此类情况配置DLQ。如果您的邮件在重试3次后无法被使用,则很可能根本不会被使用,或者需要特殊处理。
将DLQ设置在有毒消息可能到达的位置,这样您就不会丢失消息如果只有一个分区,那么当在testcase2中消费message1失败时,消费者为什么要消费message2?@herokingsley我不知道,但这就是正在发生的情况。如果在尝试
message1失败后,它不会消费message2
代码>那么我会满意的。也许会给我们看一些代码或日志helpful@herokingsley我已经在我的问题中添加了一些代码片段。testcase1的代码是什么样子的?stphngrtz没有使用原生Kafka API我同意,但SCS提供了很多抽象。无论如何,你是对的,自我管理偏移量是一种选择。如果我决定将消息放入一个特定的分区,这很可能是因为我希望它们按发生顺序进行处理。我不认为不希望消息被如此简单地跳过是一个非常独特的要求。在某些情况下可能是这样,但请提供某种配置选项来更改行为。无论如何,fr在我看来,我希望不跳过的行为是默认的。你建议的步骤会起作用,但老实说,我不相信卡夫卡和/或spring的人希望我们这么做。在分布式系统中
@SpringBootApplication
@EnableBinding(Sink.class)
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@StreamListener(Sink.INPUT)
private void consume(Message<String> message) {
log.info("Received: {}", message.getPayload());
if ("message1".equals(message.getPayload())
throw new RuntimeException();
log.info("Successfully processed message {}", message.getPayload());
}
}