From b0b25d00a6b7533e45b447308ce20209c7af9bef Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 14 Feb 2024 20:19:56 +0800 Subject: [PATCH] WIP: micrometer tracing plus eureka Signed-off-by: Adrian Cole --- build-bin/test | 2 +- webflux6-micrometer/pom.xml | 17 +++++ .../src/main/java/brave/example/Backend.java | 16 +++-- .../CustomObservationConfiguration.java | 3 +- .../src/main/java/brave/example/Frontend.java | 16 +++-- .../example/ZipkinDiscoveryConfiguration.java | 67 +++++++++++++++++++ .../src/main/resources/application.yaml | 4 ++ 7 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 webflux6-micrometer/src/main/java/brave/example/ZipkinDiscoveryConfiguration.java diff --git a/build-bin/test b/build-bin/test index e241b0b..af060ac 100755 --- a/build-bin/test +++ b/build-bin/test @@ -15,7 +15,7 @@ build-bin/docker/docker_build ${docker_image} ${VERSION} docker_compose_file=build-bin/docker-compose.test.yml case ${VERSION} in - armeria|*sleuth* ) + armeria|*sleuth*|*micrometer* ) docker_compose_file=build-bin/docker-compose-eureka.test.yml ;; armeria-kafka ) diff --git a/webflux6-micrometer/pom.xml b/webflux6-micrometer/pom.xml index 4d08164..043cc95 100644 --- a/webflux6-micrometer/pom.xml +++ b/webflux6-micrometer/pom.xml @@ -71,6 +71,23 @@ spring-boot-starter-actuator ${spring-boot.version} + + org.springframework.cloud + spring-cloud-starter-loadbalancer + 4.1.2 + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + 4.1.1 + + + + commons-logging + commons-logging + + + diff --git a/webflux6-micrometer/src/main/java/brave/example/Backend.java b/webflux6-micrometer/src/main/java/brave/example/Backend.java index ea15142..c6ae232 100644 --- a/webflux6-micrometer/src/main/java/brave/example/Backend.java +++ b/webflux6-micrometer/src/main/java/brave/example/Backend.java @@ -1,9 +1,13 @@ package brave.example; import java.time.LocalDate; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Optional; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.Import; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; @@ -12,7 +16,10 @@ @EnableAutoConfiguration @RestController -@Import(CustomObservationConfiguration.class) +@Import(value = { + CustomObservationConfiguration.class, + ZipkinDiscoveryConfiguration.class +}) public class Backend { @GetMapping("/api") @@ -24,9 +31,8 @@ public Mono printDate(@RequestHeader("user_name") Optional usern } public static void main(String[] args) { - SpringApplication.run(Backend.class, - "--spring.application.name=backend", - "--server.port=9000" - ); + new SpringApplicationBuilder(Backend.class) + .properties(ZipkinDiscoveryConfiguration.discoveryProperties()) + .run("--spring.application.name=backend", "--server.port=9000"); } } diff --git a/webflux6-micrometer/src/main/java/brave/example/CustomObservationConfiguration.java b/webflux6-micrometer/src/main/java/brave/example/CustomObservationConfiguration.java index aef0fca..aadf3d0 100644 --- a/webflux6-micrometer/src/main/java/brave/example/CustomObservationConfiguration.java +++ b/webflux6-micrometer/src/main/java/brave/example/CustomObservationConfiguration.java @@ -1,6 +1,5 @@ package brave.example; -import brave.http.HttpTracing; import io.micrometer.observation.ObservationPredicate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -21,7 +20,7 @@ public class CustomObservationConfiguration { * spring.sleuth.web.additional-skip-pattern=/health * * - *

Brave uses {@link HttpTracing#serverRequestSampler()} for server request sampling policy. + *

Brave uses {@code HttpTracing#serverRequestSampler()} for server request sampling policy. * Micrometer tracing bridges to Brave's core API, so it doesn't see or use the HTTP, Messaging or * RPC policies. Instead, it relies on its own type, {@link ObservationPredicate}, which applies * both to metrics and tracing. diff --git a/webflux6-micrometer/src/main/java/brave/example/Frontend.java b/webflux6-micrometer/src/main/java/brave/example/Frontend.java index 4b19add..883362c 100644 --- a/webflux6-micrometer/src/main/java/brave/example/Frontend.java +++ b/webflux6-micrometer/src/main/java/brave/example/Frontend.java @@ -2,8 +2,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.Import; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -12,7 +12,10 @@ @EnableAutoConfiguration @RestController -@Import(CustomObservationConfiguration.class) +@Import(value = { + CustomObservationConfiguration.class, + ZipkinDiscoveryConfiguration.class +}) public class Frontend { final WebClient webClient; @@ -26,9 +29,10 @@ public class Frontend { } public static void main(String[] args) { - SpringApplication.run(Frontend.class, - "--spring.application.name=frontend", - "--server.port=8081" - ); + new SpringApplicationBuilder(Frontend.class) + .properties(ZipkinDiscoveryConfiguration.discoveryProperties()) + .run("--spring.application.name=frontend", + "--server.port=8081" + ); } } diff --git a/webflux6-micrometer/src/main/java/brave/example/ZipkinDiscoveryConfiguration.java b/webflux6-micrometer/src/main/java/brave/example/ZipkinDiscoveryConfiguration.java new file mode 100644 index 0000000..e26846f --- /dev/null +++ b/webflux6-micrometer/src/main/java/brave/example/ZipkinDiscoveryConfiguration.java @@ -0,0 +1,67 @@ +package brave.example; + +import java.net.URI; +import java.util.LinkedHashMap; +import java.util.Map; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import zipkin2.reporter.HttpEndpointSupplier; +import zipkin2.reporter.HttpEndpointSuppliers; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty("EUREKA_SERVICE_URL") +public class ZipkinDiscoveryConfiguration { + + /** + * It is very difficult to stop loadbalancer or discovery from initializing when starters are on + * the classpath. This is a workaround to disable it when EUREKA_SERVICE_URL is not set. + */ + static Map discoveryProperties() { + Map properties = new LinkedHashMap<>(); + String eurekaServiceUrl = System.getenv("EUREKA_SERVICE_URL"); + boolean eurekaEnabled = eurekaServiceUrl != null && !eurekaServiceUrl.isEmpty(); + if (eurekaEnabled) { + properties.put("eureka.client.serviceUrl.defaultZone", eurekaServiceUrl); + } + properties.put("spring.cloud.loadbalancer.enabled", eurekaEnabled); + properties.put("spring.cloud.discovery.enabled", eurekaEnabled); + return properties; + } + + @Bean HttpEndpointSupplier.Factory loadbalancerEndpoints(LoadBalancerClient loadBalancerClient) { + LoadBalancerHttpEndpointSupplier.Factory httpEndpointSupplierFactory = + new LoadBalancerHttpEndpointSupplier.Factory(loadBalancerClient); + // don't ask more than 30 seconds (just to show) + return HttpEndpointSuppliers.newRateLimitedFactory(httpEndpointSupplierFactory, 30); + } + + record LoadBalancerHttpEndpointSupplier(LoadBalancerClient loadBalancerClient, URI virtualURL) + implements HttpEndpointSupplier { + record Factory(LoadBalancerClient loadBalancerClient) implements HttpEndpointSupplier.Factory { + + @Override public HttpEndpointSupplier create(String endpoint) { + return new LoadBalancerHttpEndpointSupplier(loadBalancerClient, URI.create(endpoint)); + } + } + + @Override public String get() { + // At least spring-cloud-netflix wants the actual hostname as a lookup value + ServiceInstance instance = loadBalancerClient.choose(virtualURL.getHost()); + if (instance != null) { + return instance.getUri() + virtualURL.getPath(); + } + throw new IllegalArgumentException( + virtualURL.getHost() + " is not an instance registered in Eureka"); + } + + @Override public void close() { + } + + @Override public String toString() { + return "LoadBalancer{" + virtualURL + "}"; + } + } +} diff --git a/webflux6-micrometer/src/main/resources/application.yaml b/webflux6-micrometer/src/main/resources/application.yaml index 09c2e65..e3f0509 100644 --- a/webflux6-micrometer/src/main/resources/application.yaml +++ b/webflux6-micrometer/src/main/resources/application.yaml @@ -34,6 +34,10 @@ management: tracing: # Note: There is no property to bind ${brave.localServiceName:${spring.application.name}} endpoint: ${zipkin.baseUrl:http://127.0.0.1:9411}/api/v2/spans +spring: + cloud: + compatibilityVerifier: + enabled: false logging: level: