Существует множество способов отслеживания проблем с производительностью и неожиданно трудоемких методов, от быстрого и простого измерения времени от начала до конца метода до более полного (и сложного) CPU flamegraph или даже классики, такой как дамп потока, профилирование процессора и т. Д.
Во многих случаях для отладки достаточно просто поработать с метками времени и сравнить их. Но с асинхронной парадигмой это может быть слишком наивным способом.
Например, в контексте RxJava такого рода утверждения в методе:
Instant start = Instant.now(); Flowable- promise = itemRepository.findAllById(idList); log.info("{}", Duration.between(start, Instant.now()); return promise;
на самом деле не будет регистрироваться время, затраченное на извлечение хранилища.
Сделать старое снова новым
К счастью, RxJava поставляется с удобным оператором для измерения того, сколько времени и наблюдений прошло с момента подписки до ее завершения: timeInterval()
(У реактора есть аналогичный оператор, о котором я расскажу позже)
временной интервал()
doc объясняет, что он будет измерять длительность в миллисекундах и заключать ранее выданное значение в объект, содержащий как значение, так и измеренную задержку. В предыдущем примере Текучий<Элемент>
стал бы Текучий<Приуроченный<Элемент>>
.
Таким образом, мы можем написать наш код следующим образом:
return itemRepository.findAllById(idList) .timeInterval() .map(timed -> { log.info("{}", timed.time()); timed.value(); });
На этот раз мы эффективно регистрируем продолжительность получения товаров.
Но все еще есть тонкость: выполнение этого на потоке может привести к фатальному получению журнала для каждого элемента, обратите внимание на общее. Что делать, если я выполняю вызовы API для множества элементов? Вот и все:
Flowable.fromIterable(idList) .map(apiClient::getItemInfo) .timeInterval() .map(timed -> { log.info("{}", timed.time()); return timed.value(); });
Мы получим журнал для каждого идентификатора, так как у нас много вызовов API и столько же подписок на .map(ApiClient::getItemInfo)
.
Вы тоже говорили о Реакторе
Реактор предоставляет свой собственный оператор, называемый истекший()
. Это очень похоже на его возврат a Поток<Кортеж2 <<<
, кортеж2, имеющий методы gett1()
(получить левое значение, здесь длительность в миллисекундах) и getT2()
(получить правильное значение, значение, выданное предыдущим вызовом).
Таким образом, приведенный выше код будет выглядеть следующим образом:
Flux.fromIterable(idList) .map(apiClient::getItemInfo) .elapsed() .map(tuple -> { log.info("{}", tuple.getT1()); return tuple.getT2(); });
Теперь представьте, что вы где-то разместили статическую длинную сводную статистику и готовы быстро экспортировать минимальные/максимальные, средние и подсчеты относительно сложного набора операций, таких как, например, вызов двух API, объединение их результатов и помещение некоторой информации в кэш Redis.
Оригинал: “https://dev.to/sourcemancer/easy-time-tracing-in-rxjava-and-reactor-2de4”