fr.pinguet62:reactor-call-stack

Parent pom providing dependency and plugin management for applications built with Maven

Лицензия

Лицензия

Категории

Категории

React Взаимодействие с пользователем Веб-фреймворки Reactor Контейнер Микросервисы Reactive libraries
Группа

Группа

fr.pinguet62
Идентификатор

Идентификатор

reactor-call-stack
Последняя версия

Последняя версия

1.0.0
Дата

Дата

Тип

Тип

jar
Описание

Описание

Parent pom providing dependency and plugin management for applications built with Maven
Система контроля версий

Система контроля версий

https://github.com/pinguet62/reactor-call-stack

Скачать reactor-call-stack

Как подключить последнюю версию

<!-- https://jarcasting.com/artifacts/fr.pinguet62/reactor-call-stack/ -->
<dependency>
    <groupId>fr.pinguet62</groupId>
    <artifactId>reactor-call-stack</artifactId>
    <version>1.0.0</version>
</dependency>
// https://jarcasting.com/artifacts/fr.pinguet62/reactor-call-stack/
implementation 'fr.pinguet62:reactor-call-stack:1.0.0'
// https://jarcasting.com/artifacts/fr.pinguet62/reactor-call-stack/
implementation ("fr.pinguet62:reactor-call-stack:1.0.0")
'fr.pinguet62:reactor-call-stack:jar:1.0.0'
<dependency org="fr.pinguet62" name="reactor-call-stack" rev="1.0.0">
  <artifact name="reactor-call-stack" type="jar" />
</dependency>
@Grapes(
@Grab(group='fr.pinguet62', module='reactor-call-stack', version='1.0.0')
)
libraryDependencies += "fr.pinguet62" % "reactor-call-stack" % "1.0.0"
[fr.pinguet62/reactor-call-stack "1.0.0"]

Зависимости

compile (4)

Идентификатор библиотеки Тип Версия
io.projectreactor : reactor-core Необязательный jar
org.springframework : spring-context Необязательный jar
org.springframework : spring-aop Необязательный jar
org.aspectj : aspectjweaver Необязательный jar 1.9.6

provided (1)

Идентификатор библиотеки Тип Версия
org.projectlombok : lombok jar 1.18.16

test (2)

Идентификатор библиотеки Тип Версия
io.projectreactor : reactor-test jar
org.springframework.boot : spring-boot-starter-test jar 2.4.0

Модули Проекта

Данный проект не имеет модулей.

Reactor Call-Stack

https://github.com/pinguet62/reactor-call-stack/actions?workflow=CI

The need of "complete call stack hierarchy"

Problem of asynchronous

It's difficult to timing the run time of an asynchronous call.
This doesn't work, because the execution is triggered at subscription:

Mono<Object> getAsyncValue() {
    long start = System.currentTimeMillis();
    Mono<Object> result = callAsync();
    long end = System.currentTimeMillis();
    System.out.println(end - start); // 0ms
    return result;
}

A solution is to subscribe to start/end events of Publisher:

Mono<Object> getAsyncValue() {
    AtomicReference<Long> start = new AtomicReference<>();
    return callAsync()
        .doOnSubscribe(x -> start.set(System.currentTimeMillis()))
        .doOnTerminate(() -> {
            long end = System.currentTimeMillis();
            System.out.println(end - start.get());
        });
}

Need of call stack

Write calls individually in logs is not exploitable:

  • insufficient: you don't know what function triggered it (need of request id)
  • verbose: all calls are listed without order or hierarchy

A call stack like following is exploitable:

Total time: 620ms /productInfo
    ⮡ 608ms getProduct
        ⮡ 210ms callDatabase
        ⮡ 305ms callStockApi
    ⮡ 343ms getPrice

Toolbox

1. Register call

It's necessary to define specific sections of stack trace.

Transformer

Use appendCallStackToMono/appendCallStackToFlux transformers:

Mono<Object> getProduct() {
    return doSomething()
        .transform(Applicator.appendCallStackToMono("getProduct"));
}

Annotation & AOP

If you whant tag the entire method result, you can use Spring support with @LogTerminateTime:

@LogTerminateTime("getProduct")
Mono<Object> getProduct() {
    return doSomething();
}

2. Handle Publisher time execution

Use the appendCallStackToMono/appendCallStackToFlux transformers:

asyncCall()
    .transform(Handler.doWithCallStackFlux(callStack -> System.out.println(callStack)))

Full example

@RestController
class SampleController {
    Mono<Product> getProduct() {
        return doSomething1()
            .transform(Applicator.appendCallStackToMono("getProduct"));
    }
    Mono<Price> getPrice() {
        return doSomething2()
            .transform(Applicator.appendCallStackToMono("getPrice"));
    }
    
    @GetMapping
    Mono<Info> getInfo() {
        return Mono.zip(
            getProduct(), getPrice(),
            (price, product) -> new Info(product, price));
    }
}

class CallStackFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(exchange)
                .transform(doWithCallStackMono(callStack -> System.out.println(callStack)));
    }
}
Total time: 620ms <root>
  ⮡ 608ms getProduct
  ⮡ 343ms getPrice

Версии библиотеки

Версия
1.0.0