Java 应用程序重新启动时卡夫卡最后偏移量增加

Java 应用程序重新启动时卡夫卡最后偏移量增加,java,akka,apache-kafka,Java,Akka,Apache Kafka,我有一个Java Akka应用程序,它读取Kafka,处理消息并手动提交 我使用的是0.10.1.1API的高级使用者 奇怪的是,当我关闭应用程序并再次启动它时,偏移量比上次提交的偏移量稍大,我找不到原因 我在代码中只有一个提交点 else if(message.getClass() == ProcessedBatches.class) { try { Logger.getRootLogger().info("[" + this.name + "/Read

我有一个Java Akka应用程序,它读取Kafka,处理消息并手动提交

我使用的是0.10.1.1API的高级使用者

奇怪的是,当我关闭应用程序并再次启动它时,偏移量比上次提交的偏移量稍大,我找不到原因

我在代码中只有一个提交点

else if(message.getClass() == ProcessedBatches.class) {
        try {
            Logger.getRootLogger().info("[" + this.name + "/Reader] Commiting ...");
            ProcessedBatches msg = (ProcessedBatches) message;
            consumer.commitSync(msg.getCommitInfo());
            lastCommitData = msg.getCommitInfo();
            lastCommit = System.currentTimeMillis();
        } catch (CommitFailedException e) {
            Logger.getRootLogger().info("[" + this.name + "/Reader] Failed to commit... Last commit: " + lastCommit + " | Last batch: " + lastBatch + ". Current uncommited messages: " + uncommitedMessages);
            self().tell(HarakiriMessage.getInstance(), self());
        }
    }
提交后,我将偏移量哈希映射保存在lastCommitData中,以便对其进行调试

然后,我添加了一个shutdownhook来打印lastCommitData变量,以检查每个分区提交的最后一个偏移量是多少

  Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        String output = 
                "############## SHUTTING DOWN CONSUMER ############### \n" + 
                lastCommitData+"\n";
        System.out.println(output);
    }));
我还有一个使用者重新平衡监听器,在使用者启动时检查每个分区的开始位置

new ConsumerRebalanceListener() {
        @Override
        public void onPartitionsRevoked(Collection<TopicPartition> collection) {}

        @Override
        public void onPartitionsAssigned(Collection<TopicPartition> collection) {
            for (TopicPartition p:collection
                 ) {
                System.out.println("Starting position "+p.toString()+":" + consumer.position(p));
            }
            coordinator.setRebalanceTimestamp(System.currentTimeMillis());
        }
    });

我不确定我做错了什么。

当您使用Consumer API轮询Kafka时,它会读取分区中上次使用的偏移量。系统中必须有其他使用者,这些使用者必须拥有您刚才停止的实例以前使用过的分区-因此,最新的偏移量会发生变化。由于您知道退出前的偏移量,因此需要将其保存到某个持久存储区-为此,请使用
ConsumerBalanceListener#onPartitionsRevoked
。重新启动使用者进程时读取该偏移量,并指示使用者从那里开始-通过调用
ConsumerBalanceListener\onPartitionsAssigned

中的
seek(分区,偏移量)
执行此操作。当使用使用者API轮询Kafka时,它从分区中上次使用的偏移量读取。系统中必须有其他使用者,这些使用者必须拥有您刚才停止的实例以前使用过的分区-因此,最新的偏移量会发生变化。由于您知道退出前的偏移量,因此需要将其保存到某个持久存储区-为此,请使用
ConsumerBalanceListener#onPartitionsRevoked
。当您重新启动使用者进程时,请阅读该偏移量,并指示您的使用者从那里开始-通过调用
ConsumerBalanceListener\onPartitionsAssigned

中的
seek(分区,偏移量)
执行此操作,请检查我们主题的保留策略
当您重新启动使用者时,可能已从分区中清除最后提交的偏移量,使用者将前进到该分区的最新偏移量。

检查本主题的保留策略
  Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        String output = 
                "############## SHUTTING DOWN CONSUMER ############### \n" + 
                lastCommitData+"\n";
        System.out.println(output);
    }));

当您重新启动您的使用者时,最后提交的偏移量可能已从分区中清除,使用者将前进到该分区的最新偏移量。

我认为您假设的“关机前偏移量:3107169023”基于关机挂钩打印的内容是否正确

  Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        String output = 
                "############## SHUTTING DOWN CONSUMER ############### \n" + 
                lastCommitData+"\n";
        System.out.println(output);
    }));
如果是,我认为有两个潜在问题

当您注册关机挂钩时,您正在关闭lastCommitData字段

因为您是从另一个线程(shutdownhook线程)访问它,所以该字段是否声明为volatile?否则,您可能正在打印过时的值

另外,java.lang.Runtime.addShutdownHook还说:

当虚拟机开始其关闭序列时,它将以未指定的顺序启动所有已注册的关闭挂钩,并让它们同时运行

因此,不能保证在关闭挂钩已经打印了lastCommitData值之后,您的使用者不会进一步提交偏移量


我建议您检查Kafka,以确定在您的应用程序关闭后,实际提交的偏移量是多少。

我认为您假设的“关闭前偏移量:3107169023”基于您的关闭挂钩打印的内容,对吗

如果是,我认为有两个潜在问题

当您注册关机挂钩时,您正在关闭lastCommitData字段

因为您是从另一个线程(shutdownhook线程)访问它,所以该字段是否声明为volatile?否则,您可能正在打印过时的值

另外,java.lang.Runtime.addShutdownHook还说:

当虚拟机开始其关闭序列时,它将以未指定的顺序启动所有已注册的关闭挂钩,并让它们同时运行

因此,不能保证在关闭挂钩已经打印了lastCommitData值之后,您的使用者不会进一步提交偏移量


我建议您检查卡夫卡,以确定在您的应用程序关闭后,实际提交的偏移量是多少。

好吧,这是该消费群体中该主题的唯一消费者,据我所知,当我关闭消费者并再次启动它时,它应该从上次提交的偏移量开始,而不是从上次使用的偏移量开始。我可以保存偏移量并进行搜索,但如果我没有错的话,我不应该在高级消费者中负责。API为您提供了保存和搜索偏移量的选项,因此这是一条有效的路径。争论对错是没有用的——它是关于需求和用例以及什么是合适的——好吧,这是该消费群体中该主题的唯一消费者,据我所知,当我关闭消费者并再次启动它时,它应该从最后一次提交的抵消开始,而不是从最后一次消费的抵消开始。我可以保存偏移量并进行搜索,但如果我没有错的话,我不应该在高级消费者中负责。API为您提供了保存和搜索偏移量的选项,因此这是一条有效的路径。争论对错是没有用的——它是关于需求和用例的,适当的不是我在队列的末尾,这个主题的保留期是4天。也不是从队列的末尾开始,只是比最后提交的位置稍微远一点。不是很多,只是一点点。这是我们端点的QP消费者,在图表上,我可以清楚地看到重新启动时的下降。卡夫卡消费者补偿主题与各个主题的保留期不同。