【问题标题】:ExchangeFilterFunction executing code outside WebClient reactive flow?ExchangeFilterFunction 在 WebClient 反应流之外执行代码?
【发布时间】:2020-03-31 21:00:41
【问题描述】:

我正在创建 ExchangeFilterFunction 以在 WebClient 请求期间进行扩展日志记录。

问题:如果我在以下示例中的.doOnNext() 函数中执行日志记录,有什么不同吗?或者它们在处理上是否相等?

ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
    LOGGER.info("Request url=" + clientResponse.url() + ", method=" + clientResponse.method());
    return Mono.just(clientRequest);
});

ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
    return Mono.just(clientRequest).doOnNext(clientResponse2 -> {
        LOGGER.info("Request url=" + clientResponse2.url() + ", method=" + clientResponse2.method());
    });
});

两者相比有什么优势吗?

【问题讨论】:

    标签: java spring spring-webflux spring-webclient


    【解决方案1】:

    我只是讨厌在您的流程中人为地引入 Mono.just 或类似内容。我想.doOnNext 是一个额外的步骤,但我无法想象它有什么不同。最好避免中断流动。我认为这将是您最糟糕的性能损失。

    注意:这将在调用WebClient 时记录,如下所述,并且通常看起来比使用便捷方法更难看,但它避免使用Mono.just

    @Override
    public void run(ApplicationArguments args) throws Exception {
        WebClient webClient = WebClient.builder()
                .baseUrl("http://localhost:8080/")
                .filter(eff)
                .build();
        webClient.get().retrieve().bodyToMono(String.class).subscribe(System.out::println);
    }
    
    ExchangeFilterFunction eff = (cr,ef)->{
        System.out.println("method: " + cr.method());
        return ef.exchange(cr)
        .map(cresp->{
            System.out.println(cresp.rawStatusCode());
            return cresp;
        });
    };
    

    【讨论】:

    • 但是我试图避免将日志显式作为步骤添加到 webclient 调用中。我宁愿用全局记录器配置 webclient 一次,然后在任何 webcall 上都不必关心日志记录,因为它是自动应用的。类似于RestTemplates 配置日志拦截器的方式。
    • 您认为您的代码在做什么?进一步阅读 baeldung 文章,您将看到许多其他可能性。那么还有其他的可能性。
    • 其实有区别,请看我的回答。
    • 这很好,但离题了。如果没有订阅,我认为 webclient 根本不会被调用。
    • 你错了。它很可能在 .switchIfEmpty() 中被 INVOKED 但从未订阅,因为其他发布者发出了一个值。我一直这样做。重点是您不(也不应该)知道如何使用发布者。您需要在订阅时做事,而不是调用。
    【解决方案2】:

    在这种特殊情况下,webclient 将正确调用过滤器。但是,请注意这一点。

    在第一种情况下,当方法被调用时会发生日志记录。如果调用者从不订阅返回的发布者,它实际上可能是不正确的。发布者也可能被多次调用,例如在重试或重复等情况下,这也是错误的。

    在第二种情况下,每次订阅时都会调用它。

    public class Foo {
    
        static Mono<String> echo(String message) {
            System.out.println("echo called with " + message);
            return Mono.just(message);
        }
    
        static Mono<String> rxEcho(String message) {
            return Mono.just(message).doOnNext(msg -> System.out.println("rxEcho was called with " + msg));
        }
    
        public static void main(String[] args) {
            //logged, even though never actually called
            echo("no sub");
            //only logs 1 time
            echo("retry")
                .repeat(3)
                .blockLast();
    
            System.out.println("---------------");
    
            //never subbed, never logged
            rxEcho("no sub");
    
            //all 4 calls are logged
            rxEcho("retry")
                .repeat(3)
                .blockLast();
        }
    }
    

    另外请注意,您不应该在实际代码中调用任何类型的 block(),我不想将该示例与 StepVerifier 混淆。

    【讨论】:

      猜你喜欢
      • 2020-03-27
      • 1970-01-01
      • 2019-09-01
      • 2023-01-22
      • 1970-01-01
      • 2016-12-25
      • 2020-01-14
      • 2017-04-04
      • 1970-01-01
      相关资源
      最近更新 更多