Further WebFlux and Messaging Reactor Sleuth improvements (#1699)
* Improving performance and adding docs
This commit is contained in:
committed by
GitHub
parent
5b98f79f92
commit
1c33332320
@@ -24,7 +24,6 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -35,8 +34,7 @@ import reactor.core.scheduler.Schedulers;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSleuthOperator;
|
||||
import org.springframework.cloud.sleuth.instrument.web.WebFluxSleuthOperators;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSleuthOperators;
|
||||
import org.springframework.cloud.stream.binder.test.InputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
@@ -45,7 +43,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -88,35 +85,42 @@ public class SleuthBenchmarkingStreamApplication {
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "simple")
|
||||
public Function<String, String> nonReactiveSimpleSleuthFunction() {
|
||||
public Function<String, String> simple() {
|
||||
System.out.println("simple_function");
|
||||
return new SimpleFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "reactive_simple")
|
||||
public Function<Flux<String>, Flux<String>> reactiveSimpleSleuthFunction() {
|
||||
public Function<Flux<String>, Flux<String>> reactiveSimple() {
|
||||
System.out.println("simple_reactive_function");
|
||||
return new SimpleReactiveFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "simple_function_with_around")
|
||||
public Function<Message<String>, Message<String>> simpleFunctionWithAround() {
|
||||
System.out.println("simple_function_with_around");
|
||||
return new SimpleMessageFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "simple_manual")
|
||||
public Function<Message<String>, Message<String>> nonReactiveSimpleManualSleuthFunction(Tracing tracing) {
|
||||
public Function<Message<String>, Message<String>> simpleManual(Tracing tracing) {
|
||||
System.out.println("simple_manual_function");
|
||||
return new SimpleManualFunction(tracing);
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "reactive_simple_manual")
|
||||
public Function<Flux<Message<String>>, Flux<Message<String>>> reactiveSimpleManualSleuthFunction(Tracing tracing) {
|
||||
public Function<Flux<Message<String>>, Flux<Message<String>>> reactiveSimpleManual(Tracing tracing) {
|
||||
System.out.println("simple_reactive_manual_function");
|
||||
return new SimpleReactiveManualFunction(tracing);
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.nonreactive.function.enabled", havingValue = "true")
|
||||
public Function<String, String> nonReactiveSleuthFunction(ExecutorService executorService) {
|
||||
public Function<String, String> nonReactiveFunction(ExecutorService executorService) {
|
||||
System.out.println("no sleuth non reactive function");
|
||||
return new SleuthNonReactiveFunction(executorService);
|
||||
}
|
||||
@@ -136,13 +140,6 @@ public class SleuthBenchmarkingStreamApplication {
|
||||
return new SleuthFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "MANUAL")
|
||||
public Function<Flux<String>, Flux<String>> manualFunction() {
|
||||
System.out.println("manual function");
|
||||
return new SleuthManualFunction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleFunction implements Function<String, String> {
|
||||
@@ -151,6 +148,7 @@ class SimpleFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
// tracing works cause headers from the input message get propagated to the output message
|
||||
log.info("Hello from simple [{}]", input);
|
||||
return input.toUpperCase();
|
||||
}
|
||||
@@ -180,18 +178,31 @@ class SimpleManualFunction implements Function<Message<String>, Message<String>>
|
||||
|
||||
@Override
|
||||
public Message<String> apply(Message<String> input) {
|
||||
return (MessagingSleuthOperator.asFunction(this.tracing, input)
|
||||
.andThen(msg -> MessagingSleuthOperator.withSpanInScope(this.tracing, msg, stringMessage -> {
|
||||
return (MessagingSleuthOperators.asFunction(this.tracing, input)
|
||||
.andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.tracing, msg, stringMessage -> {
|
||||
log.info("Hello from simple manual [{}]", stringMessage.getPayload());
|
||||
return stringMessage;
|
||||
})).andThen(msg -> MessagingSleuthOperator.afterMessageHandled(this.tracing, msg, null))
|
||||
.andThen(msg -> MessagingSleuthOperator.handleOutputMessage(this.tracing, msg))
|
||||
})).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null))
|
||||
.andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.tracing, msg))
|
||||
.andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders()))
|
||||
.andThen(msg -> MessagingSleuthOperator.afterMessageHandled(this.tracing, msg, null)).apply(input));
|
||||
.andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null)).apply(input));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleMessageFunction implements Function<Message<String>, Message<String>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleFunction.class);
|
||||
|
||||
@Override
|
||||
public Message<String> apply(Message<String> input) {
|
||||
log.info("Hello from message simple [{}]", input.getPayload());
|
||||
return MessageBuilder.withPayload(input.getPayload().toUpperCase()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// tag::simple_reactive[]
|
||||
class SimpleReactiveManualFunction implements Function<Flux<Message<String>>, Flux<Message<String>>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleReactiveFunction.class);
|
||||
@@ -204,16 +215,17 @@ class SimpleReactiveManualFunction implements Function<Flux<Message<String>>, Fl
|
||||
|
||||
@Override
|
||||
public Flux<Message<String>> apply(Flux<Message<String>> input) {
|
||||
return input.map(message -> (MessagingSleuthOperator.asFunction(this.tracing, message))
|
||||
.andThen(msg -> MessagingSleuthOperator.withSpanInScope(this.tracing, msg, stringMessage -> {
|
||||
return input.map(message -> (MessagingSleuthOperators.asFunction(this.tracing, message))
|
||||
.andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.tracing, msg, stringMessage -> {
|
||||
log.info("Hello from simple manual [{}]", stringMessage.getPayload());
|
||||
return stringMessage;
|
||||
})).andThen(msg -> MessagingSleuthOperator.afterMessageHandled(this.tracing, msg, null))
|
||||
})).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null))
|
||||
.andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders()))
|
||||
.andThen(msg -> MessagingSleuthOperator.handleOutputMessage(this.tracing, msg)).apply(message));
|
||||
.andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.tracing, msg)).apply(message));
|
||||
}
|
||||
|
||||
}
|
||||
// end::simple_reactive[]
|
||||
|
||||
class SleuthNonReactiveFunction implements Function<String, String> {
|
||||
|
||||
@@ -257,26 +269,3 @@ class SleuthFunction implements Function<Flux<String>, Flux<String>> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SleuthManualFunction implements Function<Flux<String>, Flux<String>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SleuthManualFunction.class);
|
||||
|
||||
static final Scheduler SCHEDULER = Schedulers.newParallel("sleuthManualFunction");
|
||||
|
||||
@Override
|
||||
public Flux<String> apply(Flux<String> input) {
|
||||
return input.doOnEach(WebFluxSleuthOperators.withSpanInScope(() -> log.info("Got a message")))
|
||||
.flatMap(s -> Mono.subscriberContext().delayElement(Duration.ofMillis(1), SCHEDULER).map(ctx -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(ctx, () -> log.info("Logging [{}] from flat map", s));
|
||||
return s.toUpperCase();
|
||||
})).doOnEach(signal -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(signal.getContext(), () -> log.info("Doing assertions"));
|
||||
TraceContext traceContext = signal.getContext().get(TraceContext.class);
|
||||
Assert.notNull(traceContext, "Context must be set by Sleuth instrumentation");
|
||||
Assert.state(traceContext.traceIdString().equals("4883117762eb9420"), "TraceId must be propagated");
|
||||
log.info("Assertions passed");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.SignalType;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
@@ -102,9 +103,17 @@ public class SleuthBenchmarkingSpringWebFluxApp implements ApplicationListener<R
|
||||
|
||||
@GetMapping("/simple")
|
||||
public Mono<String> simple() {
|
||||
return Mono.just("hello").map(String::toUpperCase);
|
||||
return Mono.just("hello").map(String::toUpperCase).doOnNext(s -> log.info("Hello from simple [{}]", s));
|
||||
}
|
||||
|
||||
// tag::simple_manual[]
|
||||
@GetMapping("/simpleManual")
|
||||
public Mono<String> simpleManual() {
|
||||
return Mono.just("hello").map(String::toUpperCase).doOnEach(WebFluxSleuthOperators
|
||||
.withSpanInScope(SignalType.ON_NEXT, signal -> log.info("Hello from simple [{}]", signal.get())));
|
||||
}
|
||||
// end::simple_manual[]
|
||||
|
||||
@GetMapping("/complexNoSleuth")
|
||||
public Mono<String> complexNoSleuth() {
|
||||
return Flux.range(1, 10).map(String::valueOf).collect(Collectors.toList())
|
||||
@@ -134,7 +143,7 @@ public class SleuthBenchmarkingSpringWebFluxApp implements ApplicationListener<R
|
||||
@GetMapping("/complexManual")
|
||||
public Mono<String> complexManual() {
|
||||
return Flux.range(1, 10).map(String::valueOf).collect(Collectors.toList())
|
||||
.doOnEach(WebFluxSleuthOperators.withSpanInScope(() -> log.info("Got a request")))
|
||||
.doOnEach(WebFluxSleuthOperators.withSpanInScope(SignalType.ON_NEXT, () -> log.info("Got a request")))
|
||||
.flatMap(s -> Mono.subscriberContext().delayElement(Duration.ofMillis(1), FOO_SCHEDULER).map(ctx -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(ctx, () -> log.info("Logging [{}] from flat map", s));
|
||||
return "";
|
||||
|
||||
@@ -89,9 +89,15 @@ public class MicroBenchmarkStreamTests {
|
||||
this.output = this.applicationContext.getBean(OutputDestination.class);
|
||||
}
|
||||
|
||||
private void sendInputMessage() {
|
||||
// System.out.println("Sending the message to input");
|
||||
input.send(MessageBuilder.withPayload("hello".getBytes())
|
||||
.setHeader("b3", "4883117762eb9420-4883117762eb9420-1").build());
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext initContext() {
|
||||
SpringApplication application = new SpringApplicationBuilder(SleuthBenchmarkingStreamApplication.class)
|
||||
.web(WebApplicationType.REACTIVE).application();
|
||||
.web(WebApplicationType.NONE).application();
|
||||
return application.run(runArgs());
|
||||
}
|
||||
|
||||
@@ -104,9 +110,11 @@ public class MicroBenchmarkStreamTests {
|
||||
}
|
||||
|
||||
void run() {
|
||||
// System.out.println("Sending the message to input");
|
||||
input.send(MessageBuilder.withPayload("hello".getBytes())
|
||||
.setHeader("b3", "4883117762eb9420-4883117762eb9420-1").build());
|
||||
sendInputMessage();
|
||||
assertThatOutputMessageGotReceived();
|
||||
}
|
||||
|
||||
private void assertThatOutputMessageGotReceived() {
|
||||
// System.out.println("Retrieving the message for tests");
|
||||
Message<byte[]> message = output.receive(200L);
|
||||
// System.out.println("Got the message from output");
|
||||
@@ -138,19 +146,18 @@ public class MicroBenchmarkStreamTests {
|
||||
public enum Instrumentation {
|
||||
|
||||
noSleuthSimple("spring.sleuth.enabled=false,spring.sleuth.function.type=simple"), sleuthSimple(
|
||||
"spring.sleuth.reactor.instrumentation-type=MANUAL,spring.sleuth.function.type=simple"), noSleuthReactiveSimple(
|
||||
"spring.sleuth.enabled=false,spring.sleuth.function.type=reactive_simple"), sleuthReactiveSimpleOnEach(
|
||||
"spring.sleuth.reactor.instrumentation-type=DECORATE_ON_EACH,spring.sleuth.integration.enabled=true,spring.sleuth.function.type=DECORATE_ON_EACH"),
|
||||
"spring.sleuth.function.type=simple"), sleuthSimpleWithAround(
|
||||
"spring.sleuth.function.type=simple_function_with_around"), noSleuthReactiveSimple(
|
||||
"spring.sleuth.enabled=false,spring.sleuth.function.type=reactive_simple"), sleuthReactiveSimpleManual(
|
||||
"spring.sleuth.function.type=reactive_simple_manual"), sleuthReactiveSimpleOnEach(
|
||||
"spring.sleuth.reactor.instrumentation-type=DECORATE_ON_EACH,spring.sleuth.integration.enabled=true,spring.sleuth.function.type=DECORATE_ON_EACH"),
|
||||
// This won't work with messaging
|
||||
// sleuthReactiveSimpleOnLast("spring.sleuth.reactor.instrumentation-type=DECORATE_ON_LAST,spring.sleuth.function.type=DECORATE_ON_LAST"),
|
||||
// NO FUNCTION, NO INTEGRATION, MANUAL OPERATORS
|
||||
sleuthSimpleManual(
|
||||
"spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=false,spring.sleuth.function.type=simple_manual"), sleuthReactiveSimpleManual(
|
||||
"spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=false,spring.sleuth.function.type=reactive_simple_manual"),
|
||||
// NO FUNCTION - OLD INTEGRATION STYLE
|
||||
sleuthSimpleNoFunctionInstrumentationManual(
|
||||
"spring.sleuth.function.type=simple_manual,spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=true,spring.sleuth.reactor.instrumentation-type=MANUAL"), sleuthReactiveSimpleNoFunctionInstrumentationManual(
|
||||
"spring.sleuth.function.type=reactive_simple_manual,spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=true,spring.sleuth.reactor.instrumentation-type=MANUAL");
|
||||
"spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=false,spring.sleuth.function.type=simple_manual"), sleuthSimpleNoFunctionInstrumentationManual(
|
||||
"spring.sleuth.function.type=simple_manual,spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=true,spring.sleuth.reactor.instrumentation-type=MANUAL"), sleuthReactiveSimpleNoFunctionInstrumentationManual(
|
||||
"spring.sleuth.function.type=reactive_simple_manual,spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=true,spring.sleuth.reactor.instrumentation-type=MANUAL");
|
||||
|
||||
private Set<String> entires = new HashSet<>();
|
||||
|
||||
|
||||
@@ -106,16 +106,18 @@ public class MicroBenchmarkHttpTests {
|
||||
|
||||
noSleuthSimple("spring.sleuth.enabled", "false", "/simple"), sleuthSimpleManual(
|
||||
"spring.sleuth.reactor.instrumentation-type", "MANUAL",
|
||||
"/simple"), sleuthSimpleOnEach("spring.sleuth.reactor.instrumentation-type", "DECORATE_ON_EACH",
|
||||
"/simple"), sleuthSimpleOnLast("spring.sleuth.reactor.instrumentation-type",
|
||||
"DECORATE_ON_LAST", "/simple"), noSleuthComplex("spring.sleuth.enabled", "false",
|
||||
"/complexNoSleuth"), onEachComplex(
|
||||
"spring.sleuth.reactor.instrumentation-type", "DECORATE_ON_EACH",
|
||||
"/complex"), onLastComplex(
|
||||
"/simple"), sleuthManual("spring.sleuth.reactor.instrumentation-type", "MANUAL",
|
||||
"/simpleManual"), sleuthSimpleOnEach("spring.sleuth.reactor.instrumentation-type",
|
||||
"DECORATE_ON_EACH",
|
||||
"/simple"), sleuthSimpleOnLast("spring.sleuth.reactor.instrumentation-type",
|
||||
"DECORATE_ON_LAST", "/simple"), noSleuthComplex("spring.sleuth.enabled",
|
||||
"false", "/complexNoSleuth"), onEachComplex(
|
||||
"spring.sleuth.reactor.instrumentation-type",
|
||||
"DECORATE_ON_LAST", "/complex"), onManualComplex(
|
||||
"DECORATE_ON_EACH", "/complex"), onLastComplex(
|
||||
"spring.sleuth.reactor.instrumentation-type",
|
||||
"MANUAL", "/complexManual");
|
||||
"DECORATE_ON_LAST", "/complex"), onManualComplex(
|
||||
"spring.sleuth.reactor.instrumentation-type",
|
||||
"MANUAL", "/complexManual");
|
||||
|
||||
private String key;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user