JavaSpringWebFlux,记录出站http调用所用的时间

JavaSpringWebFlux,记录出站http调用所用的时间,java,spring-webflux,Java,Spring Webflux,关于如何记录http请求所花费时间的小问题 为了避免混淆,这个问题是关于日志(而不是度量),这个问题是关于Webflux,这个问题是关于出站调用,我是客户机,试图调用服务器,我需要对这个操作计时,但从日志的角度来看 基于我使用的这个片段: @Override public Mono<String> sendOutboundRequest() { return webClient.post().retrieve().bodyToMono(String.clas

关于如何记录http请求所花费时间的小问题

为了避免混淆,这个问题是关于日志(而不是度量),这个问题是关于Webflux,这个问题是关于出站调用,我是客户机,试图调用服务器,我需要对这个操作计时,但从日志的角度来看

基于我使用的这个片段:

 @Override
    public Mono<String> sendOutboundRequest() {
        return webClient.post().retrieve().bodyToMono(String.class);
    }
但这不起作用,因为在反应式堆栈中,当执行反应式管道时,这不会计时请求的执行

请问记录时间的正确方法是什么


谢谢

您应该确保在反应式管道的执行阶段设置开始时间。您可以使用Mono.defer或Mono.fromRunnable之类的工具来实现这一点

AtomicLong start = new AtomicLong();
Mono.fromRunnable(() -> start.set(System.nanoTime())
    .then(webClient.post()...)
    .doOnNext(it -> LOGGEER.info("time spent {}",System.nanoTime() - start.get());

您应该确保在反应式管道的执行阶段设置开始时间。您可以使用Mono.defer或Mono.fromRunnable之类的工具来实现这一点

AtomicLong start = new AtomicLong();
Mono.fromRunnable(() -> start.set(System.nanoTime())
    .then(webClient.post()...)
    .doOnNext(it -> LOGGEER.info("time spent {}",System.nanoTime() - start.get());

如果希望以可重用的方式执行此操作,可以使用
ExchangeFilter函数
。您可以在

下面是一个
ExchangeFilterFunction
的示例实现,它将为
WebClient进行的每个外部api调用计时并记录结果。这一点的灵感来自于


如果希望以可重用的方式执行此操作,可以使用
ExchangeFilter函数
。您可以在

下面是一个
ExchangeFilterFunction
的示例实现,它将为
WebClient进行的每个外部api调用计时并记录结果。这一点的灵感来自于


尝试一下,非常感谢答案尝试一下,非常感谢答案很好,但我怀疑根据上下文设置开始时间是否正确。那么它不也包括所有上游订阅所需的时间吗?不,它只是乘以web客户端调用。您可以通过在web客户端调用之前或之后人为地在反应流上添加延迟来测试这一点。您应该看到,从上面的
ExchangeFilterFunction
计算的时间不包括人为延迟。如果这被证明是错误的,我们很乐意删除这个答案。延迟本身也会发生在执行时,所以这是有意义的,因为没有考虑到延迟,但是,延迟的订阅是错误的。这甚至可能不会在毫秒范围内花费时间,但如果上游确实有东西在订阅上花费更多时间,则可能会影响测量。“为了填充只能在订阅时完成的上下文,您需要使用contextWrite操作符。”和“从最终订阅开始并向上移动链。”我对两个位置应用了相同的测试。在交换过滤器功能内部,作为交换过滤器功能外部反应流的一部分。我在这两种情况下都应用了约150个平面图。在过滤器功能内部,延迟变化约2ms。在过滤器功能之外,但同一反应流的一部分的延迟变化为零。我没有看到任何证据表明计算的延迟受到ExchangeFilterFunction类之外的代码的影响。这很好,但我怀疑在上下文中设置开始时间是否正确。那么它不也包括所有上游订阅所需的时间吗?不,它只是乘以web客户端调用。您可以通过在web客户端调用之前或之后人为地在反应流上添加延迟来测试这一点。您应该看到,从上面的
ExchangeFilterFunction
计算的时间不包括人为延迟。如果这被证明是错误的,我们很乐意删除这个答案。延迟本身也会发生在执行时,所以这是有意义的,因为没有考虑到延迟,但是,延迟的订阅是错误的。这甚至可能不会在毫秒范围内花费时间,但如果上游确实有东西在订阅上花费更多时间,则可能会影响测量。“为了填充只能在订阅时完成的上下文,您需要使用contextWrite操作符。”和“从最终订阅开始并向上移动链。”我对两个位置应用了相同的测试。在交换过滤器功能内部,作为交换过滤器功能外部反应流的一部分。我在这两种情况下都应用了约150个平面图。在过滤器功能内部,延迟变化约2ms。在过滤器功能之外,但同一反应流的一部分的延迟变化为零。我没有看到任何证据表明计算的延迟受到ExchangeFilterFunction类之外的代码的影响。
@Component
public class MetricsLoggingExchangeFilterFunction implements ExchangeFilterFunction {

    private static final Logger LOGGER = LoggerFactory.getLogger(MetricsLoggingExchangeFilterFunction.class);
    private static final String METRICS_WEBCLIENT_START_TIME = MetricsLoggingExchangeFilterFunction.class.getName() + ".START_TIME";

    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        return next.exchange(request).doOnEach((signal) -> {
            if (!signal.isOnComplete()) {
                Long startTime = signal.getContextView().get(METRICS_WEBCLIENT_START_TIME);
                long duration = System.currentTimeMillis() - startTime;
                LOGGER.info("Downstream called taken {}ms", duration);
            }
        }).contextWrite(ctx -> ctx.put(METRICS_WEBCLIENT_START_TIME, System.currentTimeMillis()));
    }
}
WebClient.builder().filter(metricsLoggingExchangeFilterFunction).build()