[英]Defining a RestTemplateBuilder bean causes traceId propagation across service boundaries to no longer work
以下代碼演示了通過將RestTemplateBuilder
bean 包含到項目中, micrometer-tracing
不會跨服務邊界正確傳播traceId
。
我在這里發布示例代碼,因為我的客戶禁止我通過 GitHub 共享代碼。
創建兩個 Spring Maven 項目,demo-client 和 demo-server,參考下面的代碼,然后運行這兩個腳本讓微服務運行起來。 然后,向http://localhost:2048/tracer-response
發送 GET 請求,您將在演示客戶端日志中看到traceId
匹配。 接下來,取消注釋DemoClientApplication
中的RestTemplateBuilder
bean 並重新運行腳本。 重新測試,您會發現traceId
不再匹配。
請告知為什么此配置不再有效。 它之前使用 Spring Boot 2.7.7 和 Spring Cloud 2021.0.5。 如果 Spring 團隊的意圖是不再使用此配置並且在文檔中指出了這一點,那么我為錯過它而道歉。 如果沒有指出,那么在我看來,這應該被強調為警告,因為它清楚地否定了使用分布式跟蹤背后的全部目的。
演示客戶端代碼
package com.example.democlient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@Slf4j
public class DemoClientApplication {
public static void main(String[] args) {
SpringApplication.run(DemoClientApplication.class, args);
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
// Uncomment the bean definition below, and traceId propagation will no longer work, meaning the traceId
// values logged by demo-client and demo-server will not be the same value.
/* @Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder();
}*/
}
package com.example.democlient;
import io.micrometer.tracing.Tracer;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class DemoClientController {
@Autowired RestTemplate restTemplate;
@Autowired Tracer tracer;
@PermitAll
@GetMapping(value = "/tracer-response", produces = MediaType.APPLICATION_JSON_VALUE)
public TracerResponse getClientResponse() {
log.info("Getting the tracer response from demo server...");
var clientTraceId = tracer.currentSpan().context().traceId();
var clientSpanId = tracer.currentSpan().context().spanId();
log.info(
"Here's the traceId and spanId before the call: {} and {}", clientTraceId, clientSpanId);
var serverTracerResponse =
restTemplate
.getForEntity("http://localhost:2049/tracer-response", TracerResponse.class)
.getBody();
var serverTraceId = serverTracerResponse.getTraceId();
var serverSpanId = serverTracerResponse.getSpanId();
log.info(
"Here's the traceId and spanId from the server: {} and {}", serverTraceId, serverSpanId);
log.info("Do the client and server traceIds match? {}", clientTraceId.equals(serverTraceId));
return serverTracerResponse;
}
}
package com.example.democlient;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class TracerResponse {
private String spanId;
private String traceId;
}
src/main/resources/application.yml
server:
port: 2048
spring:
application:
name: demo-client
src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include
resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="springAppName"
source="spring.application.name"/>
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd, America/New_York} | %d{HH:mm:ss.SSS, America/New_York} | %-16.16thread | %-5.5p | %-40.40logger{40} | %-39.39(Trace: %X{traceId}) | %-22.22(Span: %X{spanId}) | %m%n"/>
<appender name="console"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<logger name="com.example" level="DEBUG" additivity="false">
<appender-ref ref="console"/>
</logger>
<logger name="org.springframework" level="WARN"
additivity="false">
<appender-ref ref="console"/>
</logger>
<root level="WARN">
<appender-ref ref="console"/>
</root>
</configuration>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-client</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
演示服務器代碼
package com.example.demoserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoServerApplication {
public static void main(String[] args) {
SpringApplication.run(DemoServerApplication.class, args);
}
}
package com.example.demoserver;
import io.micrometer.tracing.Tracer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class DemoServerController {
@Autowired
Tracer tracer;
@GetMapping(value = "/tracer-response", produces = MediaType.APPLICATION_JSON_VALUE)
public TracerResponse getClientResponse() {
log.info("Getting the serverResponse...");
log.info("traceId: {} spanId: {}",tracer.currentSpan().context().traceId(),tracer.currentSpan().context().spanId());
var response = new TracerResponse();
response.setTraceId(tracer.currentSpan().context().traceId());
response.setSpanId(tracer.currentSpan().context().spanId());
return response;
}
}
package com.example.demoserver;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
public class TracerResponse {
private String traceId;
private String spanId;
}
src/main/resources/application.yml
server:
port: 2049
spring:
application:
name: demo-server
src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include
resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="springAppName"
source="spring.application.name"/>
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd, America/New_York} | %d{HH:mm:ss.SSS, America/New_York} | %-16.16thread | %-5.5p | %-40.40logger{40} | %-39.39(Trace: %X{traceId}) | %-22.22(Span: %X{spanId}) | %m%n"/>
<appender name="console"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<logger name="com.example" level="DEBUG" additivity="false">
<appender-ref ref="console"/>
</logger>
<logger name="org.springframework" level="WARN"
additivity="false">
<appender-ref ref="console"/>
</logger>
<root level="WARN">
<appender-ref ref="console"/>
</root>
</configuration>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
管理和運行微服務的腳本
kill_demo_server.sh
#! /bin/bash
set -e
set -x
ps -ef | grep 'demo-server-0.0.1' | kill -9 $(awk 'NR==1{print $2}') 2>/dev/null;
build_deploy_demos.sh
#! /bin/bash
set -e
set -x
cd ~/repos/demo-server;
mvn clean install;
nohup java -jar target/demo-server-0.0.1-SNAPSHOT.jar &
cd ~/repos/demo-client;
mvn clean install;
java -jar target/demo-client-0.0.1-SNAPSHOT.jar
你的問題不清楚為什么你需要一個自定義的RestTemplateBuilder
bean,但我相信無論如何都有一些用例。
因此,讓我們看看是否使該 bean 完全類似於 Spring 引導自動配置所做的有助於解決您的要求!
請參閱RestTemplateAutoConfiguration
:
@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
RestTemplateBuilder builder = new RestTemplateBuilder();
return restTemplateBuilderConfigurer.configure(builder);
}
因此,您的自定義 bean 應該類似:
@Bean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
RestTemplateBuilder builder = new RestTemplateBuilder();
return restTemplateBuilderConfigurer.configure(builder);
}
RestTemplateBuilderConfigurer
委托給相應的定制器。 目前我在我的類路徑中看到這個 impls:
MockServerRestTemplateCustomizer
ObservationRestTemplateCustomizer
TraceRestTemplateCustomizer
其中ObservationRestTemplateCustomizer
是使用測微計跟蹤檢測RestTempalte
的工具。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.