技术架构

可观测性三支柱:Traces、Metrics、Logs

在微服务和云原生架构中,系统由数十甚至上百个服务组成,传统的日志排查方式已经力不从心。可观测性(Observability)成为理解系统行为的必备能力。

可观测性 ≠ 监控。监控是已知问题的检测,可观测性是未知问题的探索。

可观测性的三大支柱:

支柱 作用 典型场景 示例
Traces 请求的完整生命周期 跨服务调用链追踪 用户下单→库存扣减→支付→通知
Metrics 系统的量化指标 性能监控、容量规划 QPS=1200, P99延迟=230ms
Logs 离散的事件记录 错误排查、审计 NullPointerException at OrderService:142

OpenTelemetry是CNCF的孵化项目,目标是提供一套厂商中立的API、SDK和工具,统一采集Traces、Metrics、Logs三大信号。


OpenTelemetry架构:SDK、Collector、Exporter

OpenTelemetry采用三层架构,实现采集与导出的解耦:

┌─────────────────────────────────────────────────┐
│                   应用层                          │
│  ┌──────┐  ┌──────┐  ┌──────┐                  │
│  │ SDK  │  │ SDK  │  │ SDK  │  ← 自动/手动埋点  │
│  │(Java)│  │(Go)  │  │(Node)│                  │
│  └──┬───┘  └──┬───┘  └──┬───┘                  │
│     │         │         │                        │
│     └─────────┼─────────┘                        │
│               │ OTLP(gRPC/HTTP)                  │
│  ┌────────────▼────────────┐                    │
│  │      OTel Collector      │  ← 数据处理中枢    │
│  │  ┌──────┐┌──────┐┌────┐│                    │
│  │  │Recv  ││Proc  ││Exp ││  ← 接收→处理→导出  │
│  │  └──────┘└──────┘└────┘│                    │
│  └──────┬───────┬─────────┘                    │
│         │       │                               │
│    ┌────▼──┐ ┌──▼─────┐                        │
│    │Jaeger │ │Prometheus│  ← 后端存储           │
│    └───────┘ └─────────┘                        │
└─────────────────────────────────────────────────┘

三层模型详解

层级 组件 职责 部署位置
SDK OpenTelemetry SDK 自动/手动埋点,生成遥测数据 应用进程内
Collector OTel Collector 接收、处理、路由遥测数据 独立进程/容器
Exporter OTLP Exporter 将数据导出到后端 Collector内部

**OTLP(OpenTelemetry Protocol)**是SDK与Collector之间的通信协议,基于gRPC/HTTP,是整个架构的粘合剂。


Java Agent自动埋点:零代码侵入

OpenTelemetry Java Agent最大的魅力在于零代码侵入——只需添加一个JVM参数,即可自动追踪Spring Boot、HTTP客户端、JDBC、Redis等常用组件。

1. 下载Java Agent

curl -L -O https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

2. 启动Spring Boot应用

java -javaagent:opentelemetry-javaagent.jar \
     -Dotel.service.name=order-service \
     -Dotel.traces.exporter=otlp \
     -Dotel.metrics.exporter=otlp \
     -Dotel.logs.exporter=otlp \
     -Dotel.exporter.otlp.endpoint=http://collector:4317 \
     -Dotel.exporter.otlp.protocol=grpc \
     -jar my-app.jar

3. 常用配置项

配置项 默认值 说明
otel.service.name unknown 服务名称,标识调用链中的节点
otel.traces.exporter otlp Trace导出器:otlp/zipkin/none
otel.metrics.exporter otlp Metric导出器:otlp/prometheus/none
otel.logs.exporter otlp Log导出器:otlp/none
otel.exporter.otlp.endpoint http://localhost:4317 Collector地址
otel.instrumentation.common.enabled true 全局开关
otel.instrumentation.spring-web.enabled true Spring Web自动埋点
otel.instrumentation.jdbc.enabled true JDBC自动埋点
otel.traces.sampler parentBased_alwaysOn 采样策略

4. 自动埋点覆盖范围

组件 自动追踪 自动Metrics 说明
Spring Web MVC/WebFlux HTTP请求、Controller方法
Spring RestTemplate/WebClient 出站HTTP调用
JDBC / R2DBC 数据库查询
Redis (Lettuce/Jedis) Redis操作
Kafka 消息生产/消费
gRPC gRPC调用
Log4j2 / Logback - 日志关联TraceID

效果:一个JVM参数,即可在Jaeger中看到完整的跨服务调用链,无需修改任何业务代码。


分布式追踪实战:跨服务调用链可视化

调用链示例

用户下单的请求经过4个服务,OpenTelemetry自动将整个调用链串联:

[OrderService] POST /api/orders
  ├── [InventoryService] POST /api/inventory/deduct
  │     └── [Redis] GET inventory:product-123
  ├── [PaymentService] POST /api/payments/charge
  │     ├── [DB] INSERT INTO payments
  │     └── [Kafka] PRODUCE payment-success
  └── [NotificationService] CONSUME payment-success
        └── [SMTP] SEND order-confirmation

在Jaeger/Tempo中,这个调用链被可视化为一棵树,每个节点是一个Span,包含:

  • Span名称:如POST /api/orders
  • 持续时间:如230ms
  • Attributes:如http.method=POST, http.status_code=200
  • Events:如异常堆栈
  • Links:关联的Span(如Kafka消息的Producer→Consumer)

Trace上下文传播

OpenTelemetry通过W3C Trace Context标准在服务间传播TraceID:

GET /api/inventory/deduct HTTP/1.1
Host: inventory-service:8080
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067bf0bc902b7-01
tracestate: rojo=00f067bf0bc902b7
字段 格式 说明
version 00 W3C版本
trace-id 32位hex 全局唯一的Trace标识
parent-id 16位hex 当前Span的父Span
trace-flags 2位hex 01=采样,00=不采样

自定义Span与Attributes:业务语义埋点

自动埋点只能覆盖技术层面,业务语义需要手动添加自定义Span和Attributes。

Spring Boot集成

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>

自定义Span示例

@Service
public class OrderService {

    private final Tracer tracer;

    public OrderService(Tracer tracer) {
        this.tracer = tracer;
    }

    public OrderResult createOrder(CreateOrderRequest request) {
        Span span = tracer.spanBuilder("order.create")
            .setAttribute("order.userId", request.getUserId())
            .setAttribute("order.productCount", request.getItems().size())
            .setAttribute("order.totalAmount", request.getTotalAmount())
            .startSpan();

        try (Scope scope = span.makeCurrent()) {
            OrderResult result = doCreateOrder(request);

            span.setAttribute("order.id", result.getOrderId());
            span.setAttribute("order.status", result.getStatus().name());
            span.addEvent("order.created", Attributes.builder()
                .put("order.id", result.getOrderId())
                .build());

            return result;
        } catch (Exception e) {
            span.recordException(e);
            span.setStatus(StatusCode.ERROR, e.getMessage());
            throw e;
        } finally {
            span.end();
        }
    }
}

Attributes命名规范

前缀 语义 示例
order. 订单业务 order.id, order.status
user. 用户业务 user.id, user.tier
db. 数据库 db.operation, db.table
messaging. 消息 messaging.destination, messaging.operation
http. HTTP http.method, http.status_code

建议:遵循OpenTelemetry语义约定(Semantic Conventions),使用标准前缀和命名,便于后端工具理解。


Metrics采集:Counter、Histogram、Gauge实战

OpenTelemetry Metrics提供三种主要的仪器类型:

类型 语义 单调性 聚合方式 典型场景
Counter 只增不减的计数 单调递增 Sum 请求总数、错误总数
Histogram 值的分布统计 - Histogram 请求延迟、响应大小
Gauge 当前瞬时值 可增可减 Last Value 当前连接数、队列长度

Spring Boot Metrics示例

@Service
public class PaymentService {

    private final LongCounter requestCounter;
    private final LongHistogram latencyHistogram;
    private final DoubleGauge activeConnectionsGauge;

    public PaymentService(MeterProvider meterProvider) {
        Meter meter = meterProvider.meterBuilder("payment-service")
            .setInstrumentationVersion("1.0.0")
            .build();

        this.requestCounter = meter.counterBuilder("payment.requests.total")
            .setDescription("Total payment requests")
            .setUnit("1")
            .build();

        this.latencyHistogram = meter.histogramBuilder("payment.request.duration")
            .setDescription("Payment request duration")
            .setUnit("ms")
            .ofLongs()
            .setExplicitBucketBoundariesAdvice(
                Advice.explicitBuckets(
                    List.of(10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0)
                )
            )
            .build();

        this.activeConnectionsGauge = meter.gaugeBuilder("payment.active.connections")
            .setDescription("Active payment connections")
            .setUnit("1")
            .buildWithCallback(measurement -> {
                measurement.record(getActiveConnectionCount());
            });
    }

    public PaymentResult processPayment(PaymentRequest request) {
        long startTime = System.currentTimeMillis();

        requestCounter.add(1, Attributes.builder()
            .put("payment.method", request.getMethod().name())
            .put("payment.currency", request.getCurrency())
            .build());

        try {
            PaymentResult result = doProcessPayment(request);

            long duration = System.currentTimeMillis() - startTime;
            latencyHistogram.record(duration, Attributes.builder()
                .put("payment.method", request.getMethod().name())
                .put("http.status_code", 200)
                .build());

            return result;
        } catch (PaymentException e) {
            long duration = System.currentTimeMillis() - startTime;
            latencyHistogram.record(duration, Attributes.builder()
                .put("payment.method", request.getMethod().name())
                .put("error.type", e.getClass().getSimpleName())
                .build());

            throw e;
        }
    }
}

Collector部署模式

OTel Collector是数据处理中枢,支持三种部署模式:

模式 架构 优点 缺点 适用场景
Sidecar 每个Pod一个Collector 隔离性好,配置灵活 资源开销大 大规模K8s集群
DaemonSet 每个Node一个Collector 资源利用率高 单点故障风险 通用推荐
Gateway 独立集群 集中管理,可扩展 网络跳数多 多集群/多环境

DaemonSet模式配置

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: otel-collector
  namespace: observability
spec:
  selector:
    matchLabels:
      app: otel-collector
  template:
    metadata:
      labels:
        app: otel-collector
    spec:
      containers:
        - name: collector
          image: otel/opentelemetry-collector-contrib:0.96.0
          ports:
            - containerPort: 4317   # OTLP gRPC
            - containerPort: 4318   # OTLP HTTP
            - containerPort: 8889   # Metrics
          volumeMounts:
            - name: config
              mountPath: /etc/otelcol
      volumes:
        - name: config
          configMap:
            name: otel-collector-config

Collector配置文件

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    send_batch_size: 1024
    timeout: 5s
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
  filter:
    error_mode: ignore
    traces:
      span:
        - 'attributes["http.route"] == "/health"'
        - 'attributes["http.route"] == "/metrics"'

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
  otlp/tempo:
    endpoint: tempo:4317
  prometheusremotewrite:
    endpoint: http://prometheus:9090/api/v1/write
  elasticsearch:
    endpoints:
      - http://elasticsearch:9200
    logs_index: otel-logs

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch, filter]
      exporters: [otlp/jaeger]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [prometheusremotewrite]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [elasticsearch]

采样策略:头部采样 vs 尾部采样

在生产环境中,采集所有Trace会产生巨大的数据量和成本。采样策略是平衡可见性和成本的关键。

头部采样(Head-Based Sampling)

在Trace开始时决定是否采样,简单高效:

# 采样10%的请求
otel.traces.sampler=parentbased_traceidratio
otel.traces.sampler.arg=0.1
优点 缺点
实现简单 可能错过异常请求
无额外开销 错误请求可能被丢弃
适合高流量 无法保证错误100%采样

尾部采样(Tail-Based Sampling)

在Trace结束后根据完整信息决定是否采样,能保证错误请求100%被采集:

# Collector尾部采样配置
processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 100000
    expected_new_traces_per_sec: 100
    sampling_policies:
      - name: errors-policy
        type: status_code
        status_code:
          status_codes:
            - ERROR
      - name: slow-policy
        type: latency
        latency:
          threshold_ms: 1000
      - name: health-policy
        type: and
        and:
          sub_policies:
            - name: health-route
              type: string_attribute
              string_attribute:
                key: http.route
                values:
                  - /api/health
                invert_match: true
      - name: fallback-policy
        type: probabilistic
        probabilistic:
          sampling_percentage: 10
采样策略 规则 说明
errors-policy 状态码=ERROR 所有错误请求100%采样
slow-policy 延迟>1000ms 所有慢请求100%采样
health-policy 路由≠/api/health 排除健康检查
fallback-policy 10%概率 其他请求采样10%

推荐:生产环境使用尾部采样,保证错误和慢请求100%可见,正常请求按比例采样。


前端RUM集成:OTLP HTTP Exporter + Web Tracer

OpenTelemetry不仅适用于后端,前端RUM(Real User Monitoring)同样可以接入:

import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';

const exporter = new OTLPTraceExporter({
  url: 'https://collector.example.com/v1/traces',
  headers: {
    'X-API-Key': 'your-api-key',
  },
});

const provider = new WebTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register({ contextManager: new ZoneContextManager() });

registerInstrumentations({
  instrumentations: [
    new XMLHttpRequestInstrumentation({
      propagateTraceHeaderCorsUrls: [/api\.example\.com/],
    }),
    new FetchInstrumentation({
      propagateTraceHeaderCorsUrls: [/api\.example\.com/],
    }),
  ],
});

效果:前端发起的HTTP请求自动携带traceparent头,后端接收后继续追踪,实现从前端到后端的完整调用链。


与Prometheus + Grafana的经典组合

OpenTelemetry + Prometheus + Grafana是2026年最主流的可观测性技术栈:

OpenTelemetry SDK → OTel Collector → Prometheus → Grafana
                                     → Jaeger/Tempo → Grafana
                                     → Loki → Grafana

Prometheus暴露Metrics

# Collector配置:同时导出到Prometheus和OTLP
exporters:
  prometheus:
    endpoint: 0.0.0.0:8889
    namespace: otel
  otlp:
    endpoint: tempo:4317

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

Grafana Dashboard关键指标

面板 指标 PromQL 告警阈值
请求速率 QPS rate(http_server_request_total[5m]) >10000
P99延迟 延迟分布 histogram_quantile(0.99, rate(http_server_duration_bucket[5m])) >500ms
错误率 错误比例 rate(http_server_request_total{status=~"5.."}[5m]) / rate(http_server_request_total[5m]) >1%
饱和度 资源使用 process_runtime_jvm_memory_used / process_runtime_jvm_memory_max >85%

RED原则:Rate(请求速率)、Errors(错误率)、Duration(延迟),是服务监控的三个黄金指标。


总结

OpenTelemetry在2026年已经成为可观测性的事实标准。它的核心价值:

  1. 统一采集:一套SDK同时采集Traces、Metrics、Logs
  2. 厂商中立:不绑定任何后端,随时切换Jaeger/Tempo/Prometheus
  3. 零侵入:Java Agent自动埋点,无需修改业务代码
  4. 端到端:从前端RUM到后端微服务,完整调用链追踪
  5. 生态成熟:CNCF孵化项目,所有主流语言和框架均有SDK

可观测性不是奢侈品,而是微服务架构的必需品。 没有可观测性,微服务就是黑盒;有了OpenTelemetry,系统行为尽在掌握。

本站提供浏览器本地工具,免注册即可试用 →

#OpenTelemetry#可观测性#分布式追踪#Metrics#日志