OpenTelemetry是CNCF旗下的可观测性开源标准,统一了指标、日志和链路追踪三大信号的数据采集与传输协议。在微服务架构中,一个用户请求经过多个服务处理后返回,分布式链路追踪通过Trace ID串联整个调用链路,帮助开发者快速定位性能瓶颈和故障节点。
OpenTelemetry架构与Trace数据模型
OpenTelemetry的核心数据模型基于W3C Trace Context规范,定义了以下概念:
– Trace:一次完整的请求链路,由唯一的Trace ID标识
– Span:链路中的一个操作单元,包含操作名、起止时间、状态码、属性和事件
– SpanContext:Span的上下文信息,包含Trace ID、Span ID和Trace Flags
– Context Propagation:通过HTTP Header或gRPC Metadata在服务间传递SpanContext
OpenTelemetry架构分为三部分:API(应用代码调用的接口)、SDK(API的实现,负责采样、批处理和导出)、Collector(独立部署的数据收集与转发组件)。API和SDK运行在应用进程内,Collector作为独立进程或Sidecar部署。
Java应用自动埋点与手动Span配置
OpenTelemetry Java Agent支持零代码侵入的自动埋点,覆盖HTTP客户端、JDBC、Kafka、Redis等常用组件。通过javaagent参数启动即可自动采集链路数据:
# 下载OpenTelemetry Java Agent JAR
curl -L -o opentelemetry-javaagent.jar https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# 启动应用并注入Agent
java -javaagent:opentelemetry-javaagent.jar -Dotel.service.name=order-service -Dotel.exporter.otlp.endpoint=http://otel-collector:4317 -Dotel.traces.exporter=otlp -Dotel.metrics.exporter=none -jar order-service.jar
自动埋点覆盖不到的业务逻辑,通过手动API创建自定义Span:
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
public class OrderService {
private static final Tracer tracer =
GlobalOpenTelemetry.getTracer("order-service", "1.0.0");
public OrderResult createOrder(OrderRequest request) {
// 创建自定义Span
Span span = tracer.spanBuilder("createOrder")
.setAttribute("order.user_id", request.getUserId())
.setAttribute("order.amount", request.getAmount())
.startSpan();
try (Scope scope = span.makeCurrent()) {
// 库存校验
Span checkSpan = tracer.spanBuilder("checkInventory").startSpan();
try (Scope s = checkSpan.makeCurrent()) {
inventoryService.check(request.getProductId(), request.getQuantity());
} catch (Exception e) {
checkSpan.recordException(e);
checkSpan.setAttribute("error", true);
throw e;
} finally {
checkSpan.end();
}
// 创建订单记录
Order order = orderRepository.save(request);
span.setAttribute("order.id", order.getId());
return OrderResult.success(order);
} catch (Exception e) {
span.recordException(e);
span.setAttribute("error.type", e.getClass().getSimpleName());
throw e;
} finally {
span.end();
}
}
}
Collector部署与多后端导出配置
OpenTelemetry Collector是独立的数据管道组件,负责接收、处理和导出遥测数据。部署Collector可以解耦应用与后端存储,支持数据采样、批量处理和格式转换。
Docker Compose部署Collector的配置文件:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 512
memory_limiter:
check_interval: 2s
limit_mib: 512
spike_limit_mib: 128
filter:
traces:
error_mode: ignore
filters:
- 'attributes["http.route"] == "/actuator/health"'
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
loki:
endpoint: http://loki:3100/loki/api/v1/push
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, filter, batch]
exporters: [jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [loki]
# docker-compose.yaml
version: '3.8'
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otelcol/config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otelcol/config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8889:8889" # Prometheus metrics
jaeger:
image: jaegertracing/all-in-one:1.55
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- "16686:16686" # Jaeger UI
- "14250:14250" # gRPC
跨服务Trace传播与Baggage上下文传递
Trace在微服务间的传播依赖Context Propagation机制。HTTP服务通过W3C Trace Context标准Header传递:
// 入站请求:从HTTP Header提取Trace上下文
@RequestMapping("/api/orders")
public ResponseEntity<Order> getOrder(@RequestHeader HttpHeaders headers) {
TextMapGetter<HttpHeaders> getter = new TextMapGetter<>() {
@Override
public Iterable<String> keys(HttpHeaders carrier) {
return carrier.keySet();
}
@Override
public String get(HttpHeaders carrier, String key) {
return carrier.getFirst(key);
}
};
Context extractedContext = GlobalOpenTelemetry.getPropagators()
.getTextMapPropagator()
.extract(Context.current(), headers, getter);
try (Scope scope = extractedContext.makeCurrent()) {
Span span = tracer.spanBuilder("getOrder").startSpan();
// 在extractedContext上下文中处理请求
Order order = orderService.findById(orderId);
span.end();
return ResponseEntity.ok(order);
}
}
// 出站请求:向HTTP请求注入Trace上下文
public HttpResponse callPaymentService(Order order) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://payment-service/api/pay"))
.build();
Span clientSpan = tracer.spanBuilder("HTTP POST payment-service")
.setSpanKind(SpanKind.CLIENT)
.startSpan();
try (Scope scope = clientSpan.makeCurrent()) {
TextMapSetter<HttpRequest.Builder> setter = (carrier, key, value) ->
carrier.header(key, value);
GlobalOpenTelemetry.getPropagators()
.getTextMapPropagator()
.inject(Context.current(), request, setter);
HttpResponse response = httpClient.send(request);
clientSpan.setAttribute("http.status_code", response.statusCode());
return response;
} finally {
clientSpan.end();
}
}
Baggage机制允许在跨服务调用链中传递业务上下文信息(如用户ID、租户ID),无需修改每个服务的接口签名:
// 设置Baggage
Baggage.current().toBuilder()
.put("user.id", "12345")
.put("tenant.id", "acme")
.build()
.storeInContext(Context.current())
.makeCurrent();
// 在下游服务中读取Baggage
String userId = Baggage.current().getEntryValue("user.id");
String tenantId = Baggage.current().getEntryValue("tenant.id");
Jaeger UI链路分析与性能瓶颈定位
部署完成后,在Jaeger UI中可以查看完整的调用链路。关键分析功能包括:
– Trace Timeline:按时间线展示每个Span的起止时间和层级关系,耗时最长的Span自动标红,直观展示瓶颈位置
– Service Map:基于Trace数据自动生成微服务调用拓扑图,标注每条调用链的QPS和延迟分布
– Trace Comparison:对比正常请求与慢请求的Trace差异,快速定位异常Span
– Latency Distribution:按操作名分组展示延迟P50/P95/P99分位数
采样策略配置。生产环境全量采集Trace会产生巨大存储压力,推荐使用基于头部采样和尾部采样的混合策略:
# 尾部采样处理器配置
processors:
tail_sampling:
decision_wait: 10s
num_traces: 50000
policies:
# 采样所有错误请求
- name: error-policy
type: status_code
status_code:
status_codes: [ERROR]
# 采样延迟超过2秒的请求
- name: latency-policy
type: latency
latency:
threshold_ms: 2000
# 按比例采样正常请求
- name: probabilistic-policy
type: probabilistic
probabilistic:
sampling_percentage: 5
尾部采样需要Collector缓存完整Trace后做决策,内存开销较大。对于QPS超过10000的系统,建议先在应用侧做头部采样(10%),再在Collector侧做尾部二次过滤。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/opentelemetry-fen-bu-shi-lian-lu-zhui-zong-shi-zhan-kua-fu/