Go 接入 Zipkin 需先启动 Zipkin 服务端(如 Docker 运行 openzipkin/zipkin),再用 zipkin-go 初始化 reporter(含 HTTPCollector)、HTTPServerMiddleware 自动埋点、显式传递 context 跨 goroutine,并注意采样率、HTTP client 配置及错误标记。

Go 怎么接入 Zipkin 服务端
Zipkin 客户端本身不托管服务端,zipkin-go 只负责采集和上报,你必须先跑一个 Zipkin 服务端(比如官方的 zipkin-server JAR,或用 docker run -d -p 9411:9411 openzipkin/zipkin)。没这一步,zipkin-go 发出去的 span 全部丢弃,且默认不报错——这是最常被卡住的点。
上报地址默认是 http://localhost:9411/api/v2/spans,如果服务端换了端口或加了反向代理(比如 Nginx 前置),要显式传入 zipkin.NewHTTPCollector 的 hostPort 参数,否则请求 404 或超时,但日志里几乎不提示。
- 确保服务端已启动并能 curl 通:
curl -s http://localhost:9411/health | jq - 客户端初始化时别漏掉 collector:用
zipkin.NewReporter包一层zipkin.NewHTTPCollector,不是直接 new - 若 Zipkin 服务端启用了 Basic Auth,需手动在 HTTP client 中加
Authorizationheader,zipkin-go不自动处理
怎么给 HTTP handler 自动埋点
别手写 StartSpan 和 Finish,zipkin-go 提供了 zipkin.HTTPServerMiddleware,它会从请求头(如 X-B3-TraceId)提取上下文,并自动创建 server span。但要注意中间件顺序:必须在路由之前注册,否则无法捕获完整路径。
常见错误是把它塞在 http.HandleFunc 后面,或者用在 Gin/echo 等框架时没插对生命周期钩子——这些框架的中间件机制和原生 net/http 不同,zipkin-go 的 HTTPServerMiddleware 只适配标准库。
立即学习“go语言免费学习笔记(深入)”;
- Gin 用户该用
gin-contrib/zipkin,别硬套zipkin-go的 middleware - 确保请求带
X-B3-TraceId、X-B3-SpanId等头,否则每次都是新 trace;前端或上游调用方得透传 - 如果 handler panic,span 默认不会自动标记 error tag,得自己 recover 并调
span.Tag("error", "true")
怎么跨 goroutine 传递 trace 上下文
zipkin-go 的 Span 不是全局变量,也不绑定到 goroutine,必须显式传递 context.Context。你在主线程 start 一个 span 后,调 span.Context() 拿到带 trace 信息的 context,再传给子 goroutine 的函数参数里——漏这步,所有 go func 里的 span 都是孤立的。
典型反例:go doWork() 里直接 tracer.StartSpan(...),结果 trace 图里出现两个无关联的根 span。这不是并发问题,是 context 丢失。
- 用
zipkin.NewContext(ctx, span)把 span 注入 context,再传下去 - 子 goroutine 入口处用
zipkin.SpanFromContext(ctx)拿 span,别 new 新的 - 数据库调用、RPC 调用等 IO 操作,务必把带 span 的 context 传给 driver/client,比如
db.QueryContext(ctx, ...)
为什么本地能看到 trace,上生产就没了
大概率是采样率设太高或太低。默认 zipkin.NewTracer 用的是 zipkin.NewCountingSampler(1),即 100% 采样;但生产环境通常设成 zipkin.NewCountingSampler(1000)(千分之一),如果流量小,可能连续几十分钟都没 span 上报。
另一个隐蔽原因是 DNS 缓存或连接池复用:上报用的 HTTP client 如果没配置超时、没设 MaxIdleConnsPerHost,在容器环境里容易因 DNS 变更或服务重启导致连接卡死,后续 span 全部积压后丢弃,且无日志。
- 上线前用
zipkin.NewModuloSampler(1)强制全量采样,确认链路通不通 - 检查
zipkin.NewHTTPCollector是否用了自定义http.Client,务必设Timeout和MaxIdleConnsPerHost - 别依赖
defer span.Finish()在长生命周期 goroutine 里,span 生命周期应与业务逻辑一致,不是 defer 就安全
trace 数据丢了,往往不是代码写错了,而是 collector 连不上、采样率吃掉了、context 没传下去——这三处查完,八成就定位到了。


















