Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何测量webflux WebClient方法的执行时间?_Java_Spring_Spring Webflux_Project Reactor - Fatal编程技术网

Java 如何测量webflux WebClient方法的执行时间?

Java 如何测量webflux WebClient方法的执行时间?,java,spring,spring-webflux,project-reactor,Java,Spring,Spring Webflux,Project Reactor,我准备了一组请求,希望将这些请求与外部Web服务并行发送。 在这个流程中,我继续直接处理响应,例如将某些内容插入数据库 问题:我想跟踪一个请求的最大请求时间!,不包括加工。 但如前所述,这将只跟踪全局时间,包括任何子流程: StopWatch watch = new StopWatch(); watch.start(); Flux.fromIterable(requests) .flatMap(req -> webClient.send(req, MyResponse.class

我准备了一组请求,希望将这些请求与外部Web服务并行发送。 在这个流程中,我继续直接处理响应,例如将某些内容插入数据库

问题:我想跟踪一个请求的最大请求时间!,不包括加工。 但如前所述,这将只跟踪全局时间,包括任何子流程:

StopWatch watch = new StopWatch();
watch.start();

Flux.fromIterable(requests)
    .flatMap(req -> webClient.send(req, MyResponse.class)
            .doOnSuccess(rsp -> processResponse(rsp))) //assume some longer routine
    .collectList()
    .block();
    
watch.stop();
System.out.println(w.getTotalTimeMillis());
            

问题:除了processResponse时间之外,我如何衡量请求所用的最长时间?

Spring boot提供了一种开箱即用的功能,可以将检测添加到您的WebClient中

您可以通过使用自动配置的WebClient.Builder创建WebClient实例来启用这些指标

@Bean
public WebClient myCustomWebClient(WebClient.Builder builder) {
    return builder
            // your custom web client config code
            .build();
}
此检测将计时WebClient发出的每个API调用,并将其注册到配置的MeterRegistry中


一种选择是使用单元测试和Mockito来模拟方法processResponse的行为。然后,您只测量其他任务的时间。假设在类中有此方法:

public class AnotherService {
    public Object processResponse(Object response) {
        try {
            System.out.println("processResponse called");
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return response;
    }
}
然后使用Mockito中的when方法模拟返回。在这里你已经摆脱了时间线;来自processResponse

要在单元测试中使用它,它将如下所示:

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class GitHubJobsClientMockTest {

    @Mock
    private AnotherService anotherService;
    @InjectMocks
    private YourService yourService;

    void stackoverflowRequest() {

        Object sample = new Object();

        // HERE YOU CREATE THE MOCK OF YOUR METHOD FROM AnotherService
        when(anotherService.processResponse(any())).thenReturn(sample);

        List<Integer> pageNumbers = List.of(1, 2, 3);
        String description = "Java";

        List<Stream<Object>> result = yourService.stackoverflowRequest(pageNumbers, description);

        assertTrue(result.size() > 0);
    }
}

在mono上使用“经过”时,将返回元组的mono,其中包含经过的时间和原始对象。你必须打开它们才能使用它们。我在测试中编写了一个代码稍微简化的示例,以查看它的工作情况:

@Test
public void elapsed() {

    Flux.fromIterable(List.of(1, 2, 3, 4, 5))
        .flatMap(req -> Mono.delay(Duration.ofMillis(100L * req))
                            .map(it -> "response_" + req)
                            .elapsed()
                            .doOnNext(it -> System.out.println("I took " + it.getT1() + " MS"))
                            .map(Tuple2::getT2)
                            .doOnSuccess(rsp -> processResponse(rsp)))
        .collectList()
        .block();

}

@SneakyThrows
public void processResponse(Object it) {
    System.out.println("This is the response: " + it);
    Thread.sleep(1000);
}
输出如下所示:

I took 112 MS
This is the response: response_1
I took 205 MS
This is the response: response_2
I took 305 MS
This is the response: response_3
I took 403 MS
This is the response: response_4
I took 504 MS
This is the response: response_5
Flux.fromIterable(requests)
        .flatMap(req -> webClient.send(req, MyResponse.class)
                                 .elapsed()
                                 .doOnNext(it -> System.out.println("I took " + it.getT1() + " MS"))
                                 .map(Tuple2::getT2)
                                 .doOnSuccess(rsp -> processResponse(rsp))) //assume some longer routine
        .collectList()
        .block();
这些数字既表示延迟,在您的情况下是webClient.send,也表示反应管道本身的一点开销。它是在特定请求的flatMap运行时发生的订阅和下一次从映射发出信号(在我的情况下是映射结果,在您的情况下是webclient请求的结果)之间计算的

您的代码如下所示:

I took 112 MS
This is the response: response_1
I took 205 MS
This is the response: response_2
I took 305 MS
This is the response: response_3
I took 403 MS
This is the response: response_4
I took 504 MS
This is the response: response_5
Flux.fromIterable(requests)
        .flatMap(req -> webClient.send(req, MyResponse.class)
                                 .elapsed()
                                 .doOnNext(it -> System.out.println("I took " + it.getT1() + " MS"))
                                 .map(Tuple2::getT2)
                                 .doOnSuccess(rsp -> processResponse(rsp))) //assume some longer routine
        .collectList()
        .block();
注意:如果您想使用秒表代替,也可以执行以下操作:

Flux.fromIterable(List.of(1, 2, 3, 4, 5)).flatMap(req -> {
            StopWatch stopWatch = new StopWatch();
            return Mono.fromRunnable(stopWatch::start)
                       .then(Mono.delay(Duration.ofMillis(100L * req)).map(it -> "response_" + req).doOnNext(it -> {
                           stopWatch.stop();
                           System.out.println("I took " + stopWatch.getTime() + " MS");
                       }).doOnSuccess(this::processResponse));
        }).collectList().block();

但就我个人而言,我会推荐.appeased解决方案,因为它有点干净。

我会直接用这种方法避免秒表。而是创建一个可以在其他地方使用的度量包装器

您可以利用.doOnSubscribe、.doOnError、.doOnSuccess 但是要回答你的问题,你可以有一个类似这样的计时器

公共发送请求{ Flux.FromIterablerRequests .flatMapreq->webClient.sendreq,MyResponse.class .TransformTimerPublisher花费的时间,请求id .收藏清单 块 } //这可以通过确定它是哪种出版商来实现 //单或助焊剂 专用函数timerPublisherString度量{ stopWatch Helper秒表=新的stopWatch HelperMetric; 返回s->s.doon订阅->秒表.start .doOnSuccessdocumentRequest->stopWatch.record .Doonerror秒表::记录; } 私有类秒表助手{ 私人秒表; 私有字符串度量; 公共StopWatcherHelperString度量{ this.metric=metric; 秒表=新秒表; } 公共消费启动{ 返回s->stopWatch.start; } 公开无效记录{ 如果秒表启动了{ System.out.printlnString.formatMetric%s使用了%s、metric、stopWatch.getTime; } } 公开作废可丢弃记录{ 如果秒表启动了{ System.out.PrintLString.formatMetric%s采用了%s,报告错误为%s,公制,秒表。getTime,throwable; } } } PS:避免使用.block->它超出了目的:
您可以在内部发布服务器上使用定时操作符:您使用的是spring boot还是spring framework?@MichaelMcFadyen我使用的是spring bootalso@MartinTarjányi请举例说明如何在我的案例中使用timed或appeased。我在网上搜索了一下,但是没有这样的例子……计时和耗时的工作,除非有错误;当出现错误时,如何使用“已用”或“已计时”?以及如何访问在一个进程中发出的请求,并提取最长时间?此检测将默认显示3个指标-计数、总计和最大值。最大值将告诉您所做的最慢api调用的持续时间。您可能对错误的问题发表了评论?这个主题不是关于测试!虽然您没有提到测试,但我建议将其作为一种备选方案。使用timed而不是Opersed有什么区别吗?我注意到这两种方法大体上都是有效的,区别在于如何用元组或包装器对象返回值。两者确实以类似的方式工作