执行时间响应式编程

9

这是一种在响应式编程中查找方法 (getFavouriteDetails()) 执行时间的理想方式吗?

public List<Favourites> getFavouriteDetails(String userId){
    userService.getFavorites(userId) 
               .flatMap(favoriteService::getDetails) 
               .switchIfEmpty(suggestionService.getSuggestions()) 
               .take(5) 
               .publishOn(UiUtils.uiThreadScheduler()) 
               .subscribe(uiList::show, UiUtils::errorPopup)
               .flatMap(a -> Mono.subscriberContext().map(ctx -> {
                         log.info("Time taken : " + Duration.between(ctx.get(key), Instant.now()).toMillis() + " milliseconds.");
                         return a;
                     }))
               .subscriberContext(ctx -> ctx.put(key, Instant.now()))
}

你使用的是哪个RxJava版本?在RxJava 1下无法编译。 - Bob Dalgleish
我已经使用反应器编写了代码。 - user754657
你能使用timeInterval吗:http://reactivex.io/documentation/operators/timeinterval.html? - Pavel Molchanov
方法的执行时间是哪个? - a better oliver
获取收藏详情 - user754657
你的代码无法编译,因为它在 subscribe 后使用了 flatMap... - Simon Baslé
2个回答

8

确保只在订阅时测量执行时间的两种方法:

  • 使用flatMapMany将一个Mono包装在Flux中。这也返回一个Flux。
  • 使用AtomicReference,在onSubscribe中设置时间,并在doFinally中记录经过的时间。

示例代码 -

timeFluxV1(getFavouriteDetails(userId)).subscribe(uiList::show, UiUtils::errorPopup);

timeFluxV1(getFavouriteDetails(userId)).subscribe(uiList::show, UiUtils::errorPopup);
 
private <T>  Flux<T> timeFluxV1(Flux<T> flux) {
    return Mono.fromSupplier(System::nanoTime)
             .flatMapMany(time -> flux.doFinally(sig -> log.info("Time taken : " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - time) + " milliseconds.")));
}


private <T>  Flux<T> timeFluxV2(Flux<T> flux) {
    AtomicReference<Long> startTime = new AtomicReference<>();
    return flux.doOnSubscribe(x -> startTime.set(System.nanoTime()))
            .doFinally(x -> log.info("Time taken : " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime.get()) + " milliseconds."));
}

public Flux<Favourites> getFavouriteDetails(String userId) {
    return userService.getFavorites(userId)
               .flatMap(favoriteService::getDetails) 
               .switchIfEmpty(suggestionService.getSuggestions()) 
               .take(5) 
               .publishOn(UiUtils.uiThreadScheduler());
}

6
计时一个方法,在Java中最基本的方式是使用long System.nanoTime()。而InstantSystem.currentTimeMillis用于挂钟操作,不能保证单调性和足够的精度...
在Reactor中,为了测量序列完成所需的时间,您通常需要在订阅时开始计时(除非您订阅后什么也不会发生),并在doFinally中停止计时(它在主序列完成、错误或被取消时执行一些侧面代码)。
但是,在这里您自己进行订阅,因此不存在多个订阅的风险。因此,可以摆脱“开始订阅计时”的限制。
它给我们的东西像这样:
public List<Favourites> getFavouriteDetails(String userId){
    final long start = System.nanoTime();
    userService.getFavorites(userId) 
               .flatMap(favoriteService::getDetails) 
               .switchIfEmpty(suggestionService.getSuggestions()) 
               .take(5) 
               .publishOn(UiUtils.uiThreadScheduler())
               .doFinally(endType -> log.info("Time taken : " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) + " milliseconds."))
               .subscribe(uiList::show, UiUtils::errorPopup);
    //return needed!
}

请注意,还有一个elapsed()运算符,它测量订阅和第一个onNext之间的时间,然后是在随后的onNext之间的时间。它输出一个Flux<Tuple2<Long, T>>,您可以聚合长整型以获得总体计时,但在这种情况下,您会失去T的"实时"特性。

FlatMap将被调用5次,因为您使用了take(5)... 这基本上相当于使用更多开销的elapsed()。对于您的用例,您就像使用榴弹发射器钓鱼一样:D - Simon Baslé
3
这种方法不好,因为startnanoTime() 方法会在流的“设计时间”执行,而toMillis 方法会在流的“运行时间”执行。只有在此情况下才能正常工作,因为subscribe 正好在这里。 - EagleRainbow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接