OpenTelemetry与链路追踪的关系
OpenTelemetry(简称OTel)是CNCF主流的可观测性标准,统一了链路追踪(Tracing)、指标(Metrics)和日志(Logs)三类数据的采集规范与SDK。在后端微服务架构里,单次请求会跨多个服务、中间件与数据库,没有Trace ID就把”用户请求路径”串不起来。Java生态接入OTel,推荐直接使用Java Agent(opentelemetry-java-instrumentation),一行参数完成探针自动埋点,对业务代码零侵入。
相比自研埋点或Jaeger的直连SDK,OTel统一了数据传输格式(OTLP),一套链路数据可同时导出到Jaeger、Zipkin、SkyWalking、Datadog或自建后端,服务治理和API接口规范可以统一在Trace上落地。
Java Agent接入的两种方式与配置
方式一:启动参数加载Agent(推荐生产使用):
java -javaagent:./opentelemetry-javaagent.jar \
-Dotel.service.name=payment-service \
-Dotel.traces.exporter=otlp \
-Dotel.metrics.exporter=none \
-Dotel.logs.exporter=otlp \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-Dotel.propagators=tracecontext,baggage \
-jar app.jar
方式二:Spring Boot项目用SDK显式初始化(需要自定义埋点时):
@Configuration
public class TelemetryConfig {
@Bean(destroyMethod = "close")
public OpenTelemetry openTelemetry() {
Resource resource = Resource.getDefault().merge(
Resource.create(Attributes.of(AttributeKey.stringKey("service.name"), "payment-service")));
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(
OtlpGrpcSpanExporter.builder().setEndpoint("http://otel-collector:4317").build()).build())
.setResource(resource).build();
return OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.buildAndRegisterGlobal();
}
}
Java Agent自动支持的框架:Spring Boot/MVC、Tomcat、MySQL/PG驱动、Redis/Jedis/Lettuce、Kafka/RabbitMQ、Dubbo/Feign/gRPC等,启动即埋点。关闭一些性能占用较大的span可以用-Dotel.instrumentation.*.enabled=false(比如jdbc.enabled=false、async.enabled=false)。
自定义业务埋点与Span上下文传递
Agent覆盖不到的业务逻辑(如异步线程、消息补偿、定时任务),用OTel API手动埋点:
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
@Autowired private Tracer tracer;
public void refundOrder(Order order) {
Span span = tracer.spanBuilder("refund.order").startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("order.id", order.getId());
span.setAttribute("order.amount", order.getAmount());
doRefund(order); // 真实业务逻辑
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
throw e;
} finally {
span.end();
}
}
跨线程传递trace上下文:Context.current().wrap(Runnable/Callable);跨进程传递由Agent自动完成(HTTP头部traceparent、MQ消息头透传)。若自己写RPC框架,用TraceContextPropagator手动提取/注入traceparent头,保证链路不中断。
Trace与日志、指标的关联
后端链路追踪体系要可交叉诊断,需要:
- 日志打印带上traceId/spanId:Spring Boot + OTel Logback(opentelemetry-logback)集成后,日志上下文自动注入trace_id,可观测平台按traceId拉取整条链路日志
- Metrics:用Micrometer + OTel exporter输出jvm、线程池、数据库连接池指标,链路上报的trace可与指标对齐
- 异常栈捕获:recordException后生成error span,可在UI上看到异常所在的调用栈
为了控制存储成本,不是所有请求都存完整Trace:用Sampler(ConstantSampler全量、TraceIdRatioBased按比例抽样、ParentBased父子一致),默认推荐TailSampler配合采样规则。qps高的网关层otel.traces.sampler=parentbased_traceidratio,参数0.1(10%),业务关键服务(支付、下单)再单独提高。
常见问题与排查实践
接入OTel后最常出现的现象:
- Span树断成两个Trace:通常是RPC/HTTP透传没生效,检查traceparent头是否携带、中间网关是否剥离头(API网关要放行traceparent/tracestate)
- 收集端无数据:先测collector连通(grpchealthcheck),再确认exporter端点(4317 vs 4318),用curl模拟OTLP/HTTP探活
- 性能损耗:agent默认在1%采样下的开销约0.5%-2%,主要来自JSON序列化与DB span;批量导出并加BatchSpanProcessor队列缓冲,把ExportTimeout调大
- 自定义属性丢失:AttributeKey需要final静态且不可包含中文逗号等特殊字符,配置resource中必加service.name
接入后做一次全链路演练:发起一笔订单→观察从Nginx→网关→支付→库存→数据库的全链路是否串成一条trace,这是最有效的验证方式。Java后端在OTel标准下,可实现无死角的服务可观测性基线,后续接Prometheus告警、日志平台都走同一条采集通道。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/opentelemetry-lian-lu-zhui-zong-shi-zhan-java-ying-yong/