如果由于在消费者上错误处理消息而未发送手动确认,如何以及何时重新发送Kafka消息?

时间:2018-12-06 16:26:04

标签: spring-boot apache-kafka microservices spring-cloud-stream

我正在使用Spring Cloud Stream 1.1.2版创建/集成消费者与微服务。我将auto-commit-offset消费者属性设置为False,以便我可以在Message中接收确认标头,并在消息被成功使用后手动确认消息。

我担心的是,如果在消息使用过程中出现故障,我不会将确认发送回给代理,但是当我可以期望将相同的消息重新发送给使用者时。目前,只有在重新启动服务器后,我才能验证重新交付,如果服务器已经启动并正在运行,它将如何工作。

消费者属性设置为

kafka:
        bindings:
          input:
            consumer:
              auto-commit-offset: false
              reset-offsets: true
              start-ofofset: earliest

1 个答案:

答案 0 :(得分:1)

您需要在客户端中的消息之前查找偏移量。 偏移量在组的kafka服务以及内存中的客户端处保持不变。重启服务时,后者将丢失,这就是为什么您再次使用消息的原因。

这可以通过以下方法解决:

公共类KafkaConsumer实现ConsumerSeekAware {

        this.seekCallBack.get().seek(consumerRecord.topic(), consumerRecord.partition(), consumerRecord.offset());

希望对您有所帮助!

完整的消费者代码:

public class KafkaConsumer implements ConsumerSeekAware {

private static final String USER_TOPIC = "user-topic";
private static final String USER_LISTENER_ID = "userListener";
private static final String STRING_LISTENER = "string-listener";

private final ThreadLocal<ConsumerSeekCallback> seekCallBack = new ThreadLocal<>();

private final KafkaListenerEndpointRegistry registry;

private final TaskScheduler scheduler;

private final LocalValidatorFactoryBean validatorFactory;


public KafkaConsumer(final KafkaListenerEndpointRegistry registry, final TaskScheduler scheduler, final LocalValidatorFactoryBean validatorFactory) {
    this.registry = registry;
    this.scheduler = scheduler;
    this.validatorFactory = validatorFactory;


}

public void registerSeekCallback(ConsumerSeekAware.ConsumerSeekCallback callback) {
    this.seekCallBack.set(callback);
}

@Override
public void onPartitionsAssigned(final Map<TopicPartition, Long> assignments, final ConsumerSeekCallback callback) {

}

@Override
public void onIdleContainer(final Map<TopicPartition, Long> assignments, final ConsumerSeekCallback callback) {

}


@KafkaListener(id = USER_LISTENER_ID, topics = USER_TOPIC, containerFactory = "userContainerFactory")
public void consumeJson(ConsumerRecord<String, User> consumerRecord, User user, final Acknowledgment acknowledgment) {

   if (user.getName().equals("reject")) {
        throw new IllegalStateException("Illegal user:" + user.getName());
    }


    if (!user.getName().equals("retry")) {
        acknowledgment.acknowledge();
        log.info("Consumed JSON Message: " + user);
    } else {
        log.info("Rejected: " + user);
        this.seekCallBack.get().seek(consumerRecord.topic(), consumerRecord.partition(), consumerRecord.offset());
    }


}