理解分布式追踪
在微服务架构中,追踪是最具挑战性的部分。分布式追踪是解决这一问题的方案,它允许开发者监控和可视化请求在复杂的分布式系统中的流动。
什么是 OpenTelemetry?
OpenTelemetry 是一个与供应商无关的标准,提供了跨分布式应用程序的指标、日志和追踪功能。这有助于实现全面的可观测性,而不会被锁定在特定的监控平台中。OpenTelemetry 还提供了:
- 自动和手动的代码插桩
- 与多种后端系统的兼容性
使用 OpenTelemetry 配置 Spring Boot 3 应用程序
我们将为 Gradle 应用程序配置 OpenTelemetry。以下是我所做的更改。
项目结构:
我们将创建两个微服务:
- 订单服务(端口 8084):处理订单操作并调用价格服务
- 价格服务(端口 8083):提供价格信息
opentelemetry-spring-boot/
├── order-service/
│ ├── src/
│ └── build.gradle
├── price-service/
│ ├── src/
│ └── build.gradle
├── build.gradle
├── settings.gradle
├── docker-compose.yml
└── otel-config.yml
核心依赖:
首先,让我们了解我们使用的关键依赖项:
dependencies {
// Spring Boot core dependencies
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// OpenTelemetry core dependencies
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'io.micrometer:micrometer-tracing-bridge-otel'
implementation 'io.opentelemetry:opentelemetry-api'
implementation 'io.opentelemetry:opentelemetry-sdk'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
implementation 'io.opentelemetry.instrumentation:opentelemetry-instrumentation-api'
implementation 'io.opentelemetry:opentelemetry-exporter-logging'
}
关键依赖解释:
micrometer-tracing-bridge-otel
:将 Micrometer 追踪与 OpenTelemetry 桥接opentelemetry-api
:OpenTelemetry 核心 APIopentelemetry-sdk
:OpenTelemetry API 的实现opentelemetry-exporter-otlp
:使用 OTLP 协议导出遥测数据
服务实现:
订单服务:
@RestController
@RequestMapping("/orders")
public class OrderController {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
private final PriceGateway priceGateway;
@Autowired
public OrderController(PriceGateway priceGateway) {
this.priceGateway = priceGateway;
}
@GetMapping("/{id}")
public Order findById(@PathVariable Long id) {
// Log with trace context
LOGGER.info("Processing order request for id: {}", id);
// Call price service through gateway
Price price = priceGateway.getPrice(id);
// Create and return order
return new Order(id, 1L, ZonedDateTime.now(), price.getAmount());
}
}
价格网关(对分布式追踪至关重要):
@Component
public class PriceGateway {
private final RestTemplate restTemplate;
private static final Logger LOGGER = LoggerFactory.getLogger(PriceGateway.class);
private static final String BASE_URL = "http://localhost:8083";
@Autowired
public PriceGateway(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public Price getPrice(long productId) {
LOGGER.info("Fetching price details for product: {}", productId);
String url = String.format("%s/prices/%d", BASE_URL, productId);
try {
ResponseEntity<Price> response = restTemplate.getForEntity(url, Price.class);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
return response.getBody();
}
throw new RuntimeException("Failed to fetch price");
} catch (Exception e) {
LOGGER.error("Error fetching price: {}", e.getMessage());
throw new RuntimeException("Price service communication failed", e);
}
}
}
RestTemplate 配置(对追踪传播至关重要):
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
价格服务:
@RestController
@RequestMapping("/prices")
public class PriceController {
private final static Logger LOGGER = LoggerFactory.getLogger(PriceController.class);
@GetMapping("/{id}")
public Price findById(@PathVariable Long id) {
LOGGER.info("Retrieving price for product: {}", id);
// Simulate some processing time
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new Price(id, BigDecimal.valueOf(Math.random() * 100));
}
}
OpenTelemetry 配置
应用程序属性:
属性文件对于正确设置 OpenTelemetry 至关重要:
# Service Configuration
spring.application.name=order-service
server.port=8084
# OpenTelemetry Core Configuration
otel.service.name=${spring.application.name}
otel.exporter.otlp.endpoint=http://localhost:4317
otel.traces.exporter=otlp
otel.metrics.exporter=otlp
otel.logs.exporter=otlp
# Sampling Configuration
management.tracing.sampling.probability=1.0
# Logging Pattern (includes trace and span IDs)
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]
# Instrumentation Settings
otel.instrumentation.spring-webmvc.enabled=true
otel.instrumentation.spring-webflux.enabled=true
otel.resource.attributes=deployment.environment=development
OpenTelemetry 收集器配置:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
attributes:
actions:
- key: service.name
action: upsert
from_attribute: service.name
exporters:
logging:
loglevel: debug
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, attributes]
exporters: [logging, jaeger]
Docker 设置
服务 Dockerfile:
FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY build/libs/*.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar","app.jar"]
Docker Compose 配置:
version: '3.8'
services:
order-service:
build:
context: ./order-service
dockerfile: Dockerfile
ports:
- "8084:8084"
environment:
- SPRING_PROFILES_ACTIVE=docker
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
networks:
- otel-network
price-service:
build:
context: ./price-service
dockerfile: Dockerfile
ports:
- "8083:8083"
environment:
- SPRING_PROFILES_ACTIVE=docker
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
networks:
- otel-network
otel-collector:
image: otel/opentelemetry-collector:0.88.0
volumes:
- ./otel-config.yml:/etc/otel-collector-config.yml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8888:8888" # Prometheus metrics
networks:
- otel-network
jaeger:
image: jaegertracing/all-in-one:1.47
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- "16686:16686" # UI
- "14250:14250" # Collector
networks:
- otel-network
networks:
otel-network:
driver: bridge
现在代码更改已完成。让我们构建应用程序。
运行和测试
构建项目:./gradlew clean build
启动 Docker 容器:docker-compose up --build
验证服务是否正在运行:docker ps
运行此命令后,您可以看到类似以下的结果:
然后您需要测试端点。为此,您可以使用 curl 命令或 Postman:
curl http://localhost:8080/product/1
curl http://localhost:8081/price/1
如果 API 调用失败,您可以使用以下命令进行验证:
# View Product Service logs
docker-compose logs -f product-service
# View Price Service logs
docker-compose logs -f price-service
# View OpenTelemetry Collector logs
docker-compose logs -f otel-collector
# View Jaeger logs
docker-compose logs -f jaeger
访问 Jaeger UI:
- • 打开浏览器并导航到:
http://localhost:16686
- • 从下拉菜单中选择一个服务
- • 点击“Find Traces”查看分布式追踪
UI 如图所示。我们可以追踪最终的 API 调用到它的发起位置。这就是 OpenTelemetry 的魅力所在。
故障排除
常见问题及解决方案:
- Jaeger 中没有追踪数据:
- • 验证 OTLP 端点配置
- • 检查收集器日志:
docker-compose logs otel-collector
- • 确保采样概率设置为 1.0
- 服务无法通信:
- • 验证 docker-compose 中的网络配置
- • 检查服务端口和 URL
- • 检查服务日志:
docker-compose logs service-name
- 追踪上下文未传播:
- • 确保 RestTemplate 配置正确
- • 验证日志模式是否包含追踪 ID
- • 检查 OpenTelemetry 插桩设置
最佳实践
采样策略:
- • 在开发环境中使用 1.0
- • 根据流量调整生产环境中的采样率
日志记录:
- • 始终包含追踪 ID 和跨度 ID
- • 使用一致的日志级别
- • 在日志中添加有意义的上下文
资源属性:
- • 为追踪标记环境
- • 添加服务版本
- • 包含部署信息
错误处理:
- • 正确传播和记录错误
- • 在追踪中包含错误上下文
- • 使用适当的 HTTP 状态码
结论
分布式追踪是一种变革性的方法,用于监控微服务并理清复杂的请求流。OpenTelemetry 是一个多功能、与供应商无关的解决方案,提供了跨分布式系统的可观测性。通过将 OpenTelemetry 集成到 Spring Boot 3 应用程序中,我们可以实现端到端的追踪,从而简化调试和性能监控。借助 Jaeger 和 OpenTelemetry 收集器等工具,开发者可以更有效地可视化追踪并优化微服务。这种设置确保了可扩展且可维护的可观测性方法,这对于现代应用程序至关重要。
没有可观测性的代码就像在黑暗中航行。有了 OpenTelemetry,您不仅是在编写代码,更是在构建一个您可以理解、调试和改进的系统。祝您编码愉快,愿您的追踪永远富有洞察力!🚀📊
没有回复内容