非阻塞重试与 Spring Kafka 的集成测试

这篇具有很好参考价值的文章主要介绍了非阻塞重试与 Spring Kafka 的集成测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        如何为启用重试和死信发布的消费者的 Spring Kafka 实现编写集成测试。

Kafka 非阻塞重试

Kafka 中的非阻塞重试是通过为主主题配置重试主题来完成的。如果需要,还可以配置其他死信主题。如果所有重试均已用尽,事件将转发至 DLT。公共领域提供了大量资源来了解技术细节。 

要测试什么?

在代码中为重试机制编写集成测试时,这可能是一项具有挑战性的工作。 

  • 如何测试该事件是否已重试所需的次数? 
  • 如何测试仅在发生某些异常时才执行重试,而对于其他异常则不执行重试?
  • 如果上次重试中异常已解决,如何测试是否未进行另一次重试?
  • 在(n-1)次重试尝试失败后,如何测试重试中的第n次尝试是否成功?
  • 当所有重试尝试都用完后,如何测试事件是否已发送到死信队列?

让我们看一些代码。您可以找到很多很好的文章,展示如何使用 Spring Kafka 设置非阻塞重试。下面给出了一种这样的实现。这是使用Spring-Kafka 的@RetryableTopic@DltHandler  注释来完成的。

设置可重试消费者

@Slf4j
@Component
@RequiredArgsConstructor
public class CustomEventConsumer {

    private final CustomEventHandler handler;

    @RetryableTopic(attempts = "${retry.attempts}",
            backoff = @Backoff(
                    delayExpression = "${retry.delay}",
                    multiplierExpression = "${retry.delay.multiplier}"
            ),
            topicSuffixingStrategy = TopicSuffixingStrategy.SUFFIX_WITH_INDEX_VALUE,
            dltStrategy = FAIL_ON_ERROR,
            autoStartDltHandler = "true",
            autoCreateTopics = "false",
            include = {CustomRetryableException.class})
    @KafkaListener(topics = "${topic}", id = "${default-consumer-group:default}")
    public void consume(CustomEvent event, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
        try {
            log.info("Received event on topic {}", topic);
            handler.handleEvent(event);
        } catch (Exception e) {
            log.error("Error occurred while processing event", e);
            throw e;
        }
    }

    @DltHandler
    public void listenOnDlt(@Payload CustomEvent event) {
        log.error("Received event on dlt.");
        handler.handleEventFromDlt(event);
    }

}

如果您注意到上面的代码片段,include参数包含CustomRetryableException.class. 这告诉使用者仅在该方法抛出 CustomRetryableException 时才重试CustomEventHandler#handleEvent。您可以根据需要添加任意数量。还有一个排除参数,但一次可以使用其中任何一个参数。

${retry.attempts}在发布到 DLT 之前,事件处理应重试最多次数。

设置测试基础设施

要编写集成测试,您需要确保拥有一个正常运行的 Kafka 代理(首选嵌入式)和一个功能齐全的发布者。让我们设置我们的基础设施:

@EnableKafka
@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@EmbeddedKafka(partitions = 1,
        brokerProperties = {"listeners=" + "${kafka.broker.listeners}", 
                            "port=" + "${kafka.broker.port}"},
        controlledShutdown = true,
        topics = {"test", "test-retry-0", "test-retry-1", "test-dlt"}
)
@ActiveProfiles("test")
class DocumentEventConsumerIntegrationTest {
  
  @Autowired
  private KafkaTemplate<String, CustomEvent> testKafkaTemplate;


    // tests

}

** 配置是从 application-test.yml 文件导入的。

使用嵌入式 kafka 代理时,重要的是要提及要创建的主题。它们不会自动创建。在本例中,我们创建四个主题,即 

"test", "test-retry-0", "test-retry-1", "test-dlt"

我们已将最大重试尝试次数设置为 3 次。每个主题对应于每次重试尝试。因此,如果 3 次重试都用尽,则应将事件转发到 DLT。

测试用例

如果第一次尝试消费成功,则不应重试。

CustomEventHandler#handleEvent这可以通过该方法仅被调用一次的事实来测试。还可以添加对 Log 语句的进一步测试。

    @Test
    void test_should_not_retry_if_consumption_is_successful() throws ExecutionException, InterruptedException {
        CustomEvent event = new CustomEvent("Hello");
        // GIVEN
        doNothing().when(customEventHandler).handleEvent(any(CustomEvent.class));

        // WHEN
        testKafkaTemplate.send("test", event).get();

        // THEN
        verify(customEventHandler, timeout(2000).times(1)).handleEvent(any(CustomEvent.class));
        verify(customEventHandler, timeout(2000).times(0)).handleEventFromDlt(any(CustomEvent.class));
    }

如果引发不可重试的异常,则不应重试。

在这种情况下,该CustomEventHandler#handleEvent方法应该只调用一次:

    @Test
    void test_should_not_retry_if_non_retryable_exception_raised() throws ExecutionException, InterruptedException {
        CustomEvent event = new CustomEvent("Hello");
        // GIVEN
        doThrow(CustomNonRetryableException.class).when(customEventHandler).handleEvent(any(CustomEvent.class));

        // WHEN
        testKafkaTemplate.send("test", event).get();

        // THEN
        verify(customEventHandler, timeout(2000).times(1)).handleEvent(any(CustomEvent.class));
        verify(customEventHandler, timeout(2000).times(0)).handleEventFromDlt(any(CustomEvent.class));
    }

如果抛出 a,则重试配置的最大次数RetryableException,并在重试用完后将其发布到死信主题。

在这种情况下,该CustomEventHandler#handleEvent方法应被调用三次(maxRetries)次,并且CustomEventHandler#handleEventFromDlt该方法应被调用一次。

    @Test
    void test_should_retry_maximum_times_and_publish_to_dlt_if_retryable_exception_raised() throws ExecutionException, InterruptedException {
        CustomEvent event = new CustomEvent("Hello");
        // GIVEN
        doThrow(CustomRetryableException.class).when(customEventHandler).handleEvent(any(CustomEvent.class));

        // WHEN
        testKafkaTemplate.send("test", event).get();

        // THEN
        verify(customEventHandler, timeout(10000).times(maxRetries)).handleEvent(any(CustomEvent.class));
        verify(customEventHandler, timeout(2000).times(1)).handleEventFromDlt(any(CustomEvent.class));
    }

**在验证阶段添加了相当大的超时,以便在测试完成之前可以考虑指数退避延迟。这很重要,如果设置不当可能会导致断言失败。

应该重试直到RetryableException解决,并且如果引发不可重试的异常或消费最终成功,则不应继续重试。

测试已设置为RetryableException先抛出 a 然后再抛出 a NonRetryable exception,以便重试一次。

    @Test
    void test_should_retry_until_retryable_exception_is_resolved_by_non_retryable_exception() throws ExecutionException,
            InterruptedException {
        CustomEvent event = new CustomEvent("Hello");
        // GIVEN
        doThrow(CustomRetryableException.class).doThrow(CustomNonRetryableException.class).when(customEventHandler).handleEvent(any(CustomEvent.class));

        // WHEN
        testKafkaTemplate.send("test", event).get();

        // THEN
        verify(customEventHandler, timeout(10000).times(2)).handleEvent(any(CustomEvent.class));
        verify(customEventHandler, timeout(2000).times(0)).handleEventFromDlt(any(CustomEvent.class));
    }

    @Test
    void test_should_retry_until_retryable_exception_is_resolved_by_successful_consumption() throws ExecutionException,
            InterruptedException {
        CustomEvent event = new CustomEvent("Hello");
        // GIVEN
        doThrow(CustomRetryableException.class).doNothing().when(customEventHandler).handleEvent(any(CustomEvent.class));

        // WHEN
        testKafkaTemplate.send("test", event).get();

        // THEN
        verify(customEventHandler, timeout(10000).times(2)).handleEvent(any(CustomEvent.class));
        verify(customEventHandler, timeout(2000).times(0)).handleEventFromDlt(any(CustomEvent.class));
    }

结论

因此,您可以看到集成测试是策略、超时、延迟和验证的混合和匹配,以确保 Kafka 事件驱动架构的重试机制万无一失。文章来源地址https://www.toymoban.com/news/detail-661910.html

到了这里,关于非阻塞重试与 Spring Kafka 的集成测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring集成Kafka

    我负责的其中一个项目,接口的交互量在千万级/d,所以要存储大量的日志,为了防止日志的存储影响到系统的性能,所以在技术选型就决定了使用Kafka中间件和一个日志存储系统来负责日志的存储。 使用Kafka 的优点: 1.Kafka 是一种高吞吐量的分布式消息系统,可以支持水平

    2024年02月14日
    浏览(22)
  • Spring Boot集成Kafka详解

    Spring Boot是一个用于构建独立的、生产级的Java应用程序的框架,而Kafka是一种高吞吐量的分布式发布订阅消息系统。在本文中,我们将详细解释如何在Spring Boot项目中集成Kafka。 1. 添加依赖 首先,我们需要在项目的pom.xml文件中添加Spring Boot和Kafka的依赖。 2. 配置Kafka 接下来,

    2024年02月09日
    浏览(31)
  • Spring Cloud Stream集成Kafka

    Spring Cloud Stream是一个构建消息驱动微服务的框架,抽象了MQ的使用方式, 提供统一的API操作。Spring Cloud Stream通过Binder(绑定器)、inputs/outputs Channel完成应用程序和MQ的解耦。 Binder 负责绑定应用程序和MQ中间件,即指定应用程序是和KafKa交互还是和RabbitMQ交互或者和其他的MQ中间件交

    2024年02月13日
    浏览(39)
  • Spring Boot集成kafka的相关配置

    额外依赖只需要这一个,kafka-client 不是springboot 的东西,那是原生的 kafka 客户端, kafka-test也不需要,是用代码控制broker的东西。 也可以用java类Config 方式配置,如果没有特殊要求,可以只用spring配置的方式 注意加上@Component,被spring管理监听才有效 注意这里不能用@Value注解

    2024年02月07日
    浏览(36)
  • spring集成kafka并对消息进行监听

    需要依赖zookeeper,需提前启动 在server.properties文件中配置kafka连接zookeeper相关信息 在zookeeper.properties中配置zookeeper所需配置 kafka本地安装启动 pom文件 生产配置 消费者配置 创建topic工具类 生产业务 消费业务 消息接收类 监听类 业务处理 异步 同步 ONEWAY kafka消息发送方式有同步

    2024年02月03日
    浏览(32)
  • 在Spring Boot微服务集成kafka-clients操作Kafka集群

    记录 :463 场景 :在Spring Boot微服务集成kafka-clients-3.0.0操作Kafka集群。使用kafka-clients的原生KafkaProducer操作Kafka集群生产者Producer。使用kafka-clients的原生KafkaConsumer操作Kafka集群的消费者Consumer。 版本 :JDK 1.8,Spring Boot 2.6.3,kafka_2.12-2.8.0,kafka-clients-3.0.0。 Kafka集群安装 :https://bl

    2024年02月09日
    浏览(30)
  • 在Spring Boot微服务集成Kafka客户端(kafka-clients)操作Kafka

    记录 :459 场景 :在Spring Boot微服务集成Kafka客户端kafka-clients-3.0.0操作Kafka。使用kafka-clients的原生KafkaProducer操作Kafka生产者Producer。使用kafka-clients的原生KafkaConsumer操作Kafka的消费者Consumer。 版本 :JDK 1.8,Spring Boot 2.6.3,kafka_2.12-2.8.0,kafka-clients-3.0.0。 Kafka安装 :https://blog.csdn.ne

    2024年02月12日
    浏览(33)
  • 【Spring Boot】集成Kafka实现消息发送和订阅

    最近忙着搞低代码开发,好久没新建spring项目了,结果今天心血来潮准备建个springboot项目 注意Type选Maven,java选8,其他默认 点下一步后完成就新建了一个spring boot项目,配置下Maven环境,主要是settings.xml文件,里面要包含阿里云仓库,不然可能依赖下载不下来 在maven配置没问

    2024年02月09日
    浏览(34)
  • 从零开始学Spring Boot系列-集成Kafka

    Apache Kafka是一个开源的分布式流处理平台,由LinkedIn公司开发和维护,后来捐赠给了Apache软件基金会。Kafka主要用于构建实时数据管道和流应用。它类似于一个分布式、高吞吐量的发布-订阅消息系统,可以处理消费者网站的所有动作流数据。这种动作流数据包括页面浏览、搜

    2024年03月21日
    浏览(51)
  • kafka--技术文档--spring-boot集成基础简单使用

            查阅了很多资料了解到,使用了spring-boot中整合的kafka的使用是被封装好的。也就是说这些使用其实和在linux中的使用kafka代码的使用其实没有太大关系。但是逻辑是一样的。这点要注意! 核心配置为: 如果在下面规定了spring-boot的版本那么就不需要再使用版本号,如

    2024年02月11日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包