Swoole协程链路追踪需解决跨协程上下文传递、HTTP/gRPC透传、Span生命周期管理三大问题:须初始化全局Tracer并设唯一ServiceName;用协程ID索引Context存储Span;onRequest或中间件中解析B3头激活Span;子协程必须手动attach Span;MySQL/Redis需重写方法注入Span并显式finish。

在Swoole协程环境下实现服务间链路追踪,需解决跨协程上下文传递、HTTP/gRPC请求透传、Span生命周期管理三大核心问题,不能直接复用传统PHP-FPM的TraceID注入方式。
初始化全局Tracer与Context管理器
安装OpenTracing兼容SDK并注册全局Tracer实例,避免每次请求重复创建。
执行 composer require opentracing/opentracing:1.0.0 → 运行 php -r "require 'vendor/autoload.php'; echo class_exists('OpenTracing\Tracer') ? 'OK' : 'FAIL';" 确认类已加载。
在Swoole Server启动前初始化Tracer,使用Jaeger或Zipkin后端时必须设置【ServiceName不可为空且需全局唯一】,否则所有Span将被丢弃。
定义Context类,用SwooleCoroutine::getuid()作为协程ID索引存储Span上下文,禁止使用$_SERVER或全局变量保存TraceID。
HTTP服务端自动注入Trace上下文
方法一:通过SwooleHttpServer onRequest事件拦截
在onRequest回调中解析HTTP头X-B3-TraceId、X-B3-SpanId等字段→调用Tracer->extract()还原SpanContext→用startActiveSpan()激活新Span→将Span绑定到当前协程Context。
方法二:封装中间件(推荐)
编写TraceMiddleware类,在handle()中统一处理Header提取与Span创建→调用$next($request, $response)前将Span写入SwooleCoroutine::getContext()→响应结束前调用finish()关闭Span。
注意:若未调用Span->finish(),该Span永远不会上报,内存泄漏风险极高。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
协程内跨服务调用透传TraceID
第一步:从当前协程Context获取活跃Span
第二步:调用Tracer->inject()将SpanContext注入HTTP Header或gRPC Metadata
第三步:发起curl或SwooleCoroutineHttpClient请求时,显式设置headers参数包含B3头部
第四步:对gRPC调用,需将inject结果转为grpc_metadata数组格式,否则下游无法解析
关键点:Swoole协程切换时Context不自动继承,【必须手动在每个新协程内重新attach Span】,否则子协程生成的Span将丢失父级关系。
MySQL/Redis客户端自动打点
方法一:重写SwooleCoroutineMySQL::query()
在query()开头startActiveSpan("mysql.query")→设置tag如db.statement、db.instance→执行原生query→finish()后返回结果
方法二:使用PDO代理层
构造TracedPDO类,继承PDO→重写prepare()和execute()→在execute()内捕获SQL并注入Span tag→调用parent::execute()→finish Span
Redis同理,包装SwooleCoroutineRedis::call(),Span操作名设为redis.command,tag加redis.command和redis.key。

















