Spring Cloud Stream Kafka - 最终一致性 - Kafka会自动重试未确认的消息(使用autocommitoffset = false时)

时间:2017-02-14 16:02:42

标签: spring-cloud microservices distributed-transactions spring-cloud-stream spring-kafka

实现最终一致的分布式架构已经证明是一种痛苦。有大量的博客文章讲述了如何做的故事,但没有显示(代码)如何实际做到这一点。

我遇到的一个方面就是在没有得到消息时不得不处理消息的手动重试。

例如:我的订单服务向Kafka发送付款事件。付款服务订阅并处理它,回答付款确定或付款失败

  1. 要求付款:Order Service ----Pay event----> Kafka ----Pay Event ----> Payment Service

  2. 付款确定: - > Payment Service ----Payment ok event ----> Kafka ----Payment ok Event ----> Order Service

  3. 付款失败 - > Payment Service ----Payment failure event ----> Kafka ----Payment failure Event ----> Order Service

  4. 重点是:

    我确定通过使用同步发送消息已经发送到Kafka。但是,我必须知道付款服务处理付款的唯一方法是期待回复事件(付款确认|付款失败)。

    这迫使我在Order服务器中实现重试机制。如果在一段时间内没有得到答案,请重新尝试新的Pay事件。

    更重要的是,这也迫使我在付款服务中处理重复的邮件,以防它们实际处理但答案没有送达订单服务。

    我想知道如果消费者没有确认消息的新偏移,Kafka是否有内置机制来发送重试。

    在Spring Cloud Stream中,我们可以将autoCommitOffset属性设置为false并处理消费者中偏移的确认:

     @StreamListener(Sink.INPUT)
     public void process(Message<?> message) {
         Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
         if (acknowledgment != null) {
             System.out.println("Acknowledgment provided");
             acknowledgment.acknowledge();
         }
     }
    

    如果我们不执行acknowledgment.acknowledge();会怎样?Kafka会自动将此消息重新发送给此消费者吗?

    如果有可能,我们不需要再手动重试,可以做这样的事情:

    Paymen服务:

     @Autowired
     private PaymentBusiness paymentBusiness;
    
     @StreamListener(Sink.INPUT)
     public void process(Order order) {
         Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
         if (acknowledgment != null) {
             paymentBusiness(order);            
             //If we don't get here because of an exception 
             //Kafka would retry...
             acknowledgment.acknowledge();
         }
     }
    

    如果可以,那么在Kafka中如何配置重试期?

    在最坏的情况(也是最有可能)情况下,这不受支持,我们必须手动重试。您是否知道使用Kafka处理最终一致性的Spring Cloud Stream应用程序的任何真实示例?

1 个答案:

答案 0 :(得分:1)

  

如果我们不执行acknowirm.acknowledge(),会发生什么? Kafka会自动将消息重新发送给此消费者吗?

没有。只要客户端处于打开状态,Kafka使用者就会按顺序读取消息。 Kafka不支持更复杂的确认模式,例如单个消息确认,仅更新给定使用者组和分区主题的偏移量。 Spring Cloud Stream支持手动确认Spring Cloud Stream中的消息,以用于异步处理消息(从而防止消息丢失) - 但假设一旦手动确认消息,其偏移量将被保存,因此以前的所有消息都是相同的partition-topic将被视为&#39; read&#39;。如果要单独输出失败的消息,可以使用DLQ支持 - 并让后续的消费者接收它们。重新启动使用者将从上次保存的偏移量恢复读取,因此您可以选择不保存一系列未成功处理的消息的偏移量。

Spring Cloud Stream消费者具有内置重试和DLQ支持 - 请参阅http://docs.spring.io/spring-cloud-stream/docs/Brooklyn.SR2/reference/htmlsingle/#_kafka_consumer_properties中的enableDlq以及作为默认消费者属性的一部分提供的重试设置:http://docs.spring.io/spring-cloud-stream/docs/Brooklyn.SR2/reference/htmlsingle/#_consumer_properties