本文目录导读:

- 方案一:基于 OpenTelemetry(最推荐、标准且厂商中立)
- 方案二:基于 Spring Cloud Sleuth + Zipkin(Java 生态,Python 用适配器)
- 方案三:轻量级方案——基于 Decorator 和自定义中间件(适合非关键路径)
- 方案对比与选择建议
- 关键注意事项
- 快速启动模板(OpenTelemetry + Jaeger)
针对 Python 微服务调用链追踪(Distributed Tracing),业界已有成熟的解决方案,最主流的是基于 OpenTelemetry(简称 OTel)标准,它可以无缝对接各种后端(如 Jaeger、Zipkin、Datadog、SkyWalking 等)。
以下是几种常见且有效的实现方案,按推荐程度排序:
基于 OpenTelemetry(最推荐、标准且厂商中立)
OpenTelemetry 是 CNCF 的毕业项目,已成为云原生可观测性的标准,它通过自动插桩(Auto-instrumentation)或手动插桩来追踪跨服务的请求。
架构组件:
- 各微服务中的 OTel SDK(负责生成并导出 Span)。
- OpenTelemetry Collector(可选,作为代理/网关负责接收、处理和转发数据)。
- Trace 后端(Jaeger、Zipkin、Grafana Tempo 等,负责存储和查询)。
实现步骤(以 Jaeger 为例):
-
安装依赖(在每个微服务项目中):
pip install opentelemetry-distro opentelemetry-exporter-otlp pip install opentelemetry-instrumentation
-
自动插桩(最简单方式,适用于 Flask、Django、Requests 等): 使用命令行启动应用,它会自动注入追踪代码:
opentelemetry-instrument \ --traces_exporter otlp \ --service_name order-service \ python app.py -
手动插桩(更精细的控制): 在关键位置(如 RPC 调用、数据库操作)手动创建 Span:
from opentelemetry import trace tracer = trace.get_tracer(__name__) def process_order(order_id): with tracer.start_as_current_span("process_order") as span: span.set_attribute("order.id", order_id) # 调用下游服务 call_payment_service(order_id) -
传播上下文(关键):微服务间调用时,必须传递 Trace 上下文。
- HTTP 调用:使用
requests的opentelemetry-instrumentation-requests自动处理。 - gRPC 调用:使用
opentelemetry-instrumentation-grpc。 - 消息队列:使用
opentelemetry-instrumentation-kafka等。
- HTTP 调用:使用
-
部署 Jaeger 后端(Docker Compose 示例):
version: '3' services: jaeger: image: jaegertracing/all-in-one:latest ports: - "16686:16686" # UI - "4318:4318" # OTLP HTTP -
查看追踪:访问
http://localhost:16686,即可看到完整的调用链。
基于 Spring Cloud Sleuth + Zipkin(Java 生态,Python 用适配器)
如果公司技术栈以 Java 为主,但 Python 服务需要接入,可以使用 Pyzipkin 或 python-sleuth。
- Pyzipkin:为 Python 提供 Zipkin 兼容的 B3 传播协议。
- 原理:在 HTTP 请求头中传递
X-B3-TraceId、X-B3-SpanId等字段。
示例代码片段:
from py_zipkin.zipkin import zipkin_span
from py_zipkin.transport import BaseTransportHandler
class SimpleTransport(BaseTransportHandler):
def send(self, payload):
# 发送给 Zipkin 后端
requests.post('http://zipkin:9411/api/v2/spans', data=payload)
@app.route('/api/order')
def order():
with zipkin_span(service_name='order-service', span_name='process_order',
transport_handler=SimpleTransport()):
# 调用支付服务
requests.get('http://payment-service/api/pay')
轻量级方案——基于 Decorator 和自定义中间件(适合非关键路径)
如果不想引入复杂的 SDK,可以通过 请求头传递 TraceID + 日志关联 的方式实现简单追踪。
实现思路:
- 每个服务在入口生成一个
TraceID(UUID)。 - 通过 HTTP Header(如
X-Trace-ID)传递给下游。 - 所有日志输出带上
trace_id字段(使用structlog或python-json-logger)。 - 使用 ELK(Elasticsearch, Logstash, Kibana)等日志系统按
trace_id聚合日志。
代码示例:
import uuid
import logging
logger = logging.getLogger(__name__)
@app.before_request
def start_trace():
trace_id = request.headers.get('X-Trace-ID', str(uuid.uuid4()))
g.trace_id = trace_id
logger.info("Request started", extra={'trace_id': trace_id})
@app.after_request
def forward_trace(response):
response.headers['X-Trace-ID'] = g.trace_id
return response
缺点:无法看到精确的 Span 耗时和调用关系,只能算日志聚合。
方案对比与选择建议
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| OpenTelemetry | 标准、功能全、非侵入、支持多种后端 | 部署稍复杂(需 Collector) | 推荐首选,新项目或重构项目 |
| Pyzipkin/Zipkin | 成熟、B3 协议兼容性强 | 配置相对繁琐,社区活跃度不如 OTel | 公司已有 Zipkin 基础设施 |
| 自实现 Header 传递 | 轻量、无外部依赖 | 不可视化调用链、无法分析 Span 延时 | 小团队、非关键链路、快速排查 |
关键注意事项
- 上下文传播:最常见的坑是忘记传递 W3C TraceContext 或 B3 头,使用纯 asyncio 或 Celery 时,需要手动或通过库传播上下文。
- 采样率:生产环境不要 100% 采样,建议设置合理的采样率(如 10% 或使用动态采样策略,如
ParentBased)。 - 性能:OpenTelemetry 的自动插桩会稍微增加请求延时(通常小于 5%),但在高 QPS 下建议使用异步导出器。
- 异步框架:Flask 使用线程,FastAPI/Django ASGI 使用协程,SDK 需要区分(
opentelemetry-instrumentation-asgivsopentelemetry-instrumentation-flask)。
快速启动模板(OpenTelemetry + Jaeger)
如果你想快速跑通一个 Demo,推荐参考官方示例:
最终建议:新项目直接上 OpenTelemetry,它已经成为事实标准,并且是未来演进的方向。