[spring-projects/spring-boot]Spring Kafka 的重试主题功能添加自动配置

2024-05-14 761 views
9

这是一项使用我们今年早些时候在 Spring for Apache Kafka 2.7.0 上推出的主题功能启用延迟非阻塞重试自动配置的提案。

有关该功能的更多详细信息可以在文档中找到:https://docs.spring.io/spring-kafka/docs/current/reference/html/#retry-topic

我已经有了一份带有测试的工作草案,应该很快就会打开一个 PR,以便我们可以在必要时更详细地讨论它。

最小配置就像添加spring.kafka.retry-topic-enabled=true到应用程序 yaml 或属性配置文件一样简单,这会将默认的全局重试配置应用于所有主题。

spring:
  kafka:
    retry-topic-enabled: true

通过最小配置,框架将使用以下默认值设置主题和使用者:

  • 重试所有主题
  • 将消息发送到 DLT 之前进行 3 次处理尝试
  • 尝试之间的固定 BackOffPolicy 为 1 秒
  • 默认主题后缀“-retry-1”、“-retry-2”、“-dlt”
  • 通过 NewTopic 实例自动创建重试主题和 dlt
  • 重试所有异常
  • 使用自动配置提供的专用默认 ConcurrentKafkaListenerContainerFactory 实例,专门用于创建重试主题和 dlt 消费者
  • 使用自动配置中默认的 Spring Kafka KafkaTemplate 将消息转发到主题

配置文件中还可以提供更具体的配置,例如:

spring:
  kafka:
    retry-topic-enabled: true
    retry-topic:
      myGlobalConfiguration:
        attempts: 4
        back-off:
          delay: 300s
          multiplier: 2
          max-delay: 1200s
        topic-suffixing-strategy: suffix-with-index-value
        dlt-strategy: fail-on-error

或者,可以提供不止一种配置,以便对配置进行更细粒度的控制:

spring:
  kafka:
    retry-topic-enabled: true
    retry-topic:
      myGlobalConfiguration:
        attempts: 4
        back-off:
          delay: 300s
          multiplier: 2
          max-delay: 1200s
        topic-suffixing-strategy: suffix-with-index-value
        dlt-strategy: fail-on-error
        exclude-topics: my-specific-topic
     mySpecificConfiguration:
        include-topics: my-specific-topic
        attempts: 5
        back-off:
          delay: 5s
        not-retry-on:
          - com.mycompany.myproject.mypackage.exceptions.MyException
          - com.mycompany.myproject.mypackage.exceptions.MyOtherException
        topic-suffixing-strategy: suffix-with-index-value
        dlt-strategy: fail-on-error

注意:如果对于许多主题配置来说过于冗长,用户应该能够使用标准 Spring Boot 配置功能(例如spring.config.import.

如果您对此有任何反馈,我将不胜感激。如果有任何问题或需要我提供帮助,请告诉我。

@garyrussell 和@artembilan,如果可能的话,我也非常感谢您的反馈。

感谢大家为精彩的 Spring 项目付出的辛勤工作!

回答

2

@tomazfernandes 我同意现在是时候考虑通过 Spring Boot 添加对自动配置的支持了;事实证明,该功能非常受欢迎,并且在社区中越来越受欢迎(根据收到的反馈)。

如果您可以展示什么是最小配置(具有合理的默认值),这可能会对启动团队有所帮助,因为上面的内容乍一看有点令人不知所措。

现在将其纳入 Boot 2.6 可能为时已晚,但我会听从 Boot 团队的决定。

8

@garyrussell 感谢您一如既往的反馈和支持。我更新了描述,使最小配置更加清晰 - 它应该像添加spring.kafka.retry-topic-enabled=true到应用程序 yaml 或属性配置文件一样简单。

更大的 yaml 将适用于更具体的配置 - 如果有助于理解该功能,我也可以提供更小的示例。

我已将默认值添加到说明中。

1

恕我直言,YAML 太大、太复杂,无法被视为微服务的普通常规配置。 Spring Boot 的目标是为我们提供尽可能多的默认形状,并为那些静态暴露的 bean 提供少量的外部配置。您的代码显示的内容类似于 YAML 编程,它不如Customizer目标项目中提供自动配置的某些模式实现那么有吸引力。

可能没问题。但可能没有这些分组:让其保持简单并兼容微服务!

不管怎样,谢谢你的巨大努力,@tomazfernandes!

1

您好@artembilan,非常感谢您的反馈!我明白你的观点,并且绝对值得研究如何精简 yaml 并提供更多 OOTB。

我在描述中提供的示例是一个故意复杂的配置,以展示我们可能拥有的一些可能性。我根据 Spring Kafka 的 Github 和 Stack Overflow 中打开的问题研究了一些用户重试主题的配置,因此我们可以看到更接近现实生活中的示例,以及他们的特定 YAML 配置如何使用这种方法。

来自https://github.com/spring-projects/spring-kafka/discussions/1921

spring:
  kafka:
    retry-topic-enabled: true
    retry-topic:
      globalConfiguration:
        attempts: 4
        back-off:
          delay: 300s
          multiplier: 2
          max-delay: 1200s
        topic-suffixing-strategy: suffix-with-index-value
        dlt-strategy: fail-on-error

来自https://github.com/spring-projects/spring-kafka/issues/1863

spring:
  kafka:
    retry-topic-enabled: true
    retry-topic:
      globalConfiguration:
        attempts: 2
        back-off:
          delay: 1s
          multiplier: 2
        topic-suffixing-strategy: suffix-with-index-value
        fixed-delay-topic-strategy: single-topic
        listener-container-factory: retryKafkaListenerContainerFactory

来自https://stackoverflow.com/questions/69571401/dlt-message-conspiration

spring:
  kafka:
    retry-topic-enabled: true
    retry-topic:
      globalConfiguration:
        attempts: 5
        dlt-handler-class: com.mycompany.myproject.mypackage.MyClass
        dlt-handler-method: xyz
        back-off:
          delay: 1s
          multiplier: 2
          max-delay: 3s
        not-retry-on:
          - com.mycompany.myproject.mypackage.exceptions.MyException
          - com.mycompany.myproject.mypackage.exceptions.MyOtherException
        listener-container-factory: kafkaListenerContainerFactory
        topic-suffixing-strategy: suffix-with-index-value

我不确定启动标准中可接受的 yaml 配置大小是多少 - 这些示例一开始对我来说似乎很合理,而且我肯定有比这个大的配置文件,例如用于外部服务,甚至带有 ssl、模式注册表的 Spring Kafka 本身、主题、消费者、生产者等。当然,配置越复杂、越细粒度,配置文件就会越大。

在我看来,大多数时候用户的重试主题配置无论如何都应该在配置文件中外部化——要么在项目本身的 application.yml 文件上,要么使用 Spring Cloud Config。

因此,当前外部化配置的用户必须自己完成所有映射和实例化,可能通过RetryTopicConfiguration bean 的类@Value上的注释,或者通过SpEL 的注释为每个主题进行。也许这可以帮助他们解决问题,只需编写一次配置,而不必担心其他任何事情。@Configuration@RetryableTopic

7

我已经用(希望)不那么压倒性的例子更新了描述。

2

感谢您提出这个问题,@tomazfernandes。

将其纳入 Boot 2.6 可能为时已晚,但我会听从 Boot 团队的决定

是的,已经达到了 RC,添加新功能已经太晚了,尤其是像这个一样可能很复杂的功能。感觉像是应该进入早期里程碑,以便它可以根据用户反馈进行发展。

查看上面的一些 YAML 示例,似乎所提议的内容会将任意数量的 YAML 映射到任意数量的RetryTopicConfigurationbean。虽然这是可能的,但我们通常会在 Boot 中尽量避免这种情况。

这里与自动配置 OAuth 2 客户端的支持有一些相似之处。不过,我认为不同的是,无论用户配置中定义了多少个客户端,都只定义了一个 bean:

https://github.com/spring-projects/spring-boot/blob/9cb5f035e79942baf7efeff0a23686a6ccdc6a61/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/客户端/servlet/OAuth2ClientRegistrationRepositoryConfiguration.java#L45-L51

如果我们想为此追求自动配置,并且假设这还不可能,我认为如果可以在 Spring Kafka 中以不那么重的方式配置重试主题,那就太好了。

3

您好@wilkinsona,非常感谢您的反馈!

是的,已经达到了 RC,添加新功能已经太晚了,尤其是像这个一样可能很复杂的功能。感觉像是应该进入早期里程碑,以便它可以根据用户反馈进行发展。

我同意,这种合同发布后要修改要复杂得多,我们没必要着急。虽然我承认昨天我对此有点担心?

查看上面的一些 YAML 示例,似乎所提议的内容会将任意数量的 YAML 映射到任意数量的RetryTopicConfigurationbean。虽然这是可能的,但我们通常会在 Boot 中尽量避免这种情况。

您将 YAML 的每个部分映射到 RetryTopicConfiguration 实例是正确的 - 从编码角度来看,这看起来是一个简单而有效的策略。尽管 @artembilan 提出了一些关于我正在研究的合同本身的好观点。

我确实明白你的观点,完全有道理。从框架的角度来看,如果每个功能都依赖于实例化无限数量的 bean,那么它可能很快就会变得一团糟。我想这至少部分是由于缺乏通过常规方法实例化 bean 列表的方法而强制执行的@Bean

我的想法是在 Spring Kafka 中为这些实例创建一个聚合类,以便我们(和用户)可以将多个 RetryTopicConfiguration 实例添加到单个聚合实例中,该聚合实例可以通过常规方法进行注册@Bean。听上去怎么样?

另外,我并不真正期望用户为每个应用程序创建大量配置,特别是如果我们谈论的微服务可能根本就不会消耗这么多主题。但是,如果这种让用户在 Spring Kafka 中为每个配置定义一个 bean 的策略从长远来看看起来像是一个问题,那么我们可以考虑弃用当前的方法并仅依赖于单个聚合 bean,如果这是我们的方式的话。重新出发。

9

我的想法是在 Spring Kafka 中为这些实例创建一个聚合类……可以通过常规@Bean方法注册。听上去怎么样?

这听起来不错。这与我上面提到的 Spring Security 的 OAuth 支持中采用的方法相匹配。聚合类类似于InMemoryClientRegistrationRepository作为 bean 被使用的类。

另一种方法是类似于 Spring MVC 的WebMvcConfigurer方法,该方法通过某种可以注册配置的重试主题注册表来调用。ResourceHandlerRegistry例如,这类似于 MVC 。

我对 Spring Kafka 的编程模型还不够熟悉,不知道其中哪一个与其 API 的其余部分更一致,但我认为无论是RetryTopicConfiguration从 Spring Boot 自动配置的角度还是从 Spring Boot 的自动配置和以编程方式自行配置事物的用户。

3

我对 Spring Kafka 的编程模型还不够熟悉,不知道其中哪一个与其 API 的其余部分更一致,但我认为无论是RetryTopicConfiguration从 Spring Boot 自动配置的角度还是从 Spring Boot 的自动配置和以编程方式自行配置事物的用户。

Spring Kafka 使用配置器/注册器/注册表方法来处理@KafkaListener注释,然后注册端点并创建容器。创建和配置侦听器的另一种方法是手动实例化容器或ListenerContainerFactory.

如果我理解正确的话,在这种模式中,注册表进行实际处理,配置器使用访问者模式在处理开始之前将自定义应用到注册表。

对于 RetryTopic 设置,当KafkaListenerAnnotationBeanPostProcessor处理@KafkaListener bean 时,会RetryTopicConfigurationProvider尝试从应用程序上下文中注册的 RTC bean 或从带注释的方法@RetryableTopic中的注释为当前正在处理的主题提供配置实例@KafkaListener。如果找到给定主题的配置,它将由另一个类处理,该类协调重试和 dlt 端点注册。

考虑到 RT 配置处理本身已经由一个单独的类处理,该类期望接收正确的 RTC 实例,因此采用存储库方法可能更有意义,拥有一个存储库 bean,在尝试执行操作时,RTCProvider 会查找该存储库 bean。提供配置。说得通?

按照 ClientConfigurationRepository 中的模式,我们可能有:

public interface RetryTopicConfigurationRepository {

    @Nullable RetryTopicConfiguration findByTopicNames(Collection<String> topics);

}
0

实际上,我发现在某些情况下,注册表似乎具有更主动的角色(KafkaListenerEndpointRegistry、LoggingMeterRegistry),而在某些情况下,它则具有更多的被动角色(InterceptorRegistry、ResourceHandlerRegistry)。我不太熟悉这些配置方法,我会继续研究它们。

4

非常感谢@wilkinsona 提供的示例,我已经起草了每个示例的解决方案,并且它们都带来了可靠的设计!

在考虑了所介绍的内容之后,我想提出一种不同的自动配置方法。如果我们决定继续这样做,我们可以将此方法扩展到喜欢在 Spring Kafka 中以编程方式执行此操作的用户,这也有助于保留大量配置逻辑。

正如 @garyrussell 所建议的,我将从最简单的示例开始,然后从那里开始逐步完善。

最简单的仍然是:

spring:
  kafka:
    retry-topic-enabled: true

这将为该功能设置合理的默认值,并将其应用于所有主题。如果当前的默认值不理想,我们可以稍后讨论这些默认值。

用户可以使用以下命令覆盖默认值:

spring:
    kafka:
        retry-topic-enabled: true
        attempts: 5
        back-off:
            delay: 1s
            multiplier: 2
            max-delay: 3s

正如 @artembilan 建议的那样,这些组已经消失了 - 这些覆盖将适用于所有主题。

如果用户想要覆盖特定主题的配置,我们可以:

spring:
    kafka:
        retry-topic-enabled: true
        attempts: 5
        back-off:
            delay: 1s
            multiplier: 2
            max-delay: 3s
        topic-overrides:
            my-specific-topic:
                dlt:
                    class: com.mycompany.myproject.mypackage.MyDltHandlerClass
                    method: myDltMethod
            my-specific-topics:
                matching: *-specific-topics-suffix   
                back-off:
                    delay: 2s

通过这种方式,我认为我们可以获得一个更小、更直观的合同。

您对此感觉如何,它看起来适合自动配置吗?

谢谢!

1

是的,我认为现在看起来不错且简洁,特别是对于没有任何特定于主题的覆盖的情况。

0

我个人认为我们不应该添加配置的“topic-overrides”部分。我们通常会尽量避免在配置属性中指定类和方法。 @tomazfernandes 你有时间为配置的第一部分提供 PR 吗?

7

嗨@snicoll,感谢您对此进行调查。

我个人认为我们不应该添加配置的“topic-overrides”部分。我们通常会尽量避免在配置属性中指定类和方法。

可以,当然。到目前为止,我从这次对话中了解到,Spring Boot 的自动配置侧重于配置框架的组件并让用户覆盖一些合理的默认值,而不是让用户进行特定的配置,例如针对特定主题或主题组。

对于这些,用户仍然需要手动将任何外部化配置连接到@Configuration类或@KafkaListener注释。我最初认为让自动配置处理所有接线是一件好事,但现在我发现它可能很容易变得不必要的混乱。

我的理解正确吗?

如果是这样,我的问题是,对于此功能,除了添加具有给定属性的全局配置之外,我们是否希望自动配置覆盖用户可能添加的任何其他重试主题配置的默认值?

也许@garyrussell 可以插话一下这个?

@tomazfernandes 你有时间为配置的第一部分提供 PR 吗?

是的,当然,我会有时间,并会立即开始调查这个问题,谢谢。我真的很感谢大家花时间调查此事。

1

我总体上同意@wilkinsona 和@snicoll 的评论;让我们只进行基本配置(启用、尝试、退避,也许还可以选择添加异常类型分类 - 可重试和不可重试)。

对于更高级的配置,用户配置他/她自己的构建器类来创建配置会更有意义,因为 IDE 提供了所有内容辅助功能。https://docs.spring.io/spring-kafka/docs/current/reference/html/#using-retrytopicconfiguration-beans

一旦我们有了可用的基本配置,我们就可以等待用户反馈并根据具体情况考虑添加。

9

当然@garyrussell,完全有道理。非常感谢您的反馈。综上所述,我认为对自动配置应该做什么以及不做什么有了更好的理解。 @artembilan 对 YAML 编程的第一个评论现在也更加清晰了。

看来我已经有足够的钱开始做这件事了。我应该很快就会准备好公关,如果有人有任何其他疑虑或建议,请告诉我。

谢谢!

5

结束有利于 PR #29812