Further WebFlux and Messaging Reactor Sleuth improvements (#1699)

* Improving performance and adding docs
This commit is contained in:
Marcin Grzejszczak
2020-07-28 18:02:16 +02:00
committed by GitHub
parent 5b98f79f92
commit 1c33332320
18 changed files with 479 additions and 151 deletions

View File

@@ -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");
});
}
}

View File

@@ -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 "";

View File

@@ -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<>();

View File

@@ -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;