OpenTelemetry链路追踪实战:Java应用全链路可观测性体系搭建

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/

(0)
小编小编
上一篇 2小时前
下一篇 2小时前

相关推荐