Java ApacheKafka-出现错误时是否可能丢失消息?

Java ApacheKafka-出现错误时是否可能丢失消息?,java,apache-kafka,spring-cloud-stream,Java,Apache Kafka,Spring Cloud Stream,我正在深入研究并观察一些行为,这些行为让我怀疑我是否做错了什么,或者它是否按预期工作——我几乎不怀疑: 出错时可能会丢失消息 我的设置尽可能简单。一个Kafka代理和一个只有1个分区的主题。具有默认设置的代理、主题、生产者和消费者(自动确认为true) 测试用例1 生成message1 生成message2 启动一个使用者,该使用者在接收任何消息时抛出RuntimeException 正在使用消息1,请重试 正在使用消息1,请重试 正在使用消息1,请重试 异常被抛出 正在使用消息2,请重试 正

我正在深入研究并观察一些行为,这些行为让我怀疑我是否做错了什么,或者它是否按预期工作——我几乎不怀疑:

出错时可能会丢失消息

我的设置尽可能简单。一个Kafka代理和一个只有1个分区的主题。具有默认设置的代理、主题、生产者和消费者(自动确认为true)

测试用例1

  • 生成
    message1
  • 生成
    message2
  • 启动一个使用者,该使用者在接收任何消息时抛出RuntimeException
  • 正在使用
    消息1
    ,请重试
  • 正在使用
    消息1
    ,请重试
  • 正在使用
    消息1
    ,请重试
  • 异常被抛出
  • 正在使用
    消息2
    ,请重试
  • 正在使用
    消息2
    ,请重试
  • 正在使用
    消息2
    ,请重试
  • 异常被抛出
  • 停止并重新启动消费者
  • 正在使用
    消息1
    ,请重试
  • 正在使用
    消息1
    ,请重试
  • 正在使用
    消息1
    ,请重试
  • 异常被抛出
  • 正在使用
    消息2
    ,请重试
  • 正在使用
    消息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());
        }
    }