1. 概述

随着 Spring WebFlux 的引入,我们有了一个强大的新工具来构建响应式、非阻塞的应用程序。虽然使用这项技术比以前更加简单,但在 Spring WebFlux 中调试响应式序列却并不轻松

在这篇简短的文章中,我们将探讨如何在异步序列中轻松地记录事件,并避免一些常见的错误。

2. Maven 依赖

首先,在项目中添加 Spring WebFlux 的依赖,以便我们可以创建响应式流:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

你可以从 Maven Central 获取最新的 spring-boot-starter-webflux 依赖。

3. 创建响应式流

接下来,我们使用 Flux 创建一个响应式流,并调用 .log() 方法来启用日志记录:

Flux<Integer> reactiveStream = Flux.range(1, 5).log();

然后,我们通过订阅来消费生成的数据:

reactiveStream.subscribe();

4. 响应式流的日志输出

运行上述代码后,我们会看到如下日志输出:

2018-11-11 22:37:04 INFO | onSubscribe([Synchronous Fuseable] FluxRange.RangeSubscription)
2018-11-11 22:37:04 INFO | request(unbounded)
2018-11-11 22:37:04 INFO | onNext(1)
2018-11-11 22:37:04 INFO | onNext(2)
2018-11-11 22:37:04 INFO | onNext(3)
2018-11-11 22:37:04 INFO | onNext(4)
2018-11-11 22:37:04 INFO | onNext(5)
2018-11-11 22:37:04 INFO | onComplete()

可以看到流中发生的每一个事件:5 个值被发出,随后流通过 onComplete() 事件关闭。

5. 更复杂的日志场景

我们可以通过修改代码来观察更复杂的场景。例如,给 Flux 添加 .take() 操作符,限制只获取前三个事件:

Flux<Integer> reactiveStream = Flux.range(1, 5).log().take(3);

执行后输出如下:

2018-11-11 22:45:35 INFO | onSubscribe([Synchronous Fuseable] FluxRange.RangeSubscription)
2018-11-11 22:45:35 INFO | request(unbounded)
2018-11-11 22:45:35 INFO | onNext(1)
2018-11-11 22:45:35 INFO | onNext(2)
2018-11-11 22:45:35 INFO | onNext(3)
2018-11-11 22:45:35 INFO | cancel()

可以看到,.take() 使得流在发出三个事件后调用了 cancel()

⚠️ 注意:.log() 的位置非常关键。如果我们把 .log() 放在 .take() 之后,输出会有所不同:

Flux<Integer> reactiveStream = Flux.range(1, 5).take(3).log();

输出如下:

2018-11-11 22:49:23 INFO | onSubscribe([Fuseable] FluxTake.TakeFuseableSubscriber)
2018-11-11 22:49:23 INFO | request(unbounded)
2018-11-11 22:49:23 INFO | onNext(1)
2018-11-11 22:49:23 INFO | onNext(2)
2018-11-11 22:49:23 INFO | onNext(3)
2018-11-11 22:49:23 INFO | onComplete()

这次流仍然发出了三个事件,但日志中显示的是 onComplete() 而不是 cancel()。✅ 这是因为我们观察的是 .take() 的输出结果,而不是它内部请求的行为。

6. 总结

本文中我们介绍了如何使用内置的 .log() 方法对响应式流进行日志记录。合理使用 .log(),可以极大提升调试响应式程序的效率。

一如既往,本文的完整示例代码可以在 GitHub 上找到。


原始标题:Logging a Reactive Sequence