链路追踪(OpenTelemetry)
概述
tracer是基于 go.opentelemetry.io/otel封装的组件,为微服务提供链路追踪能力。
使用示例
初始化 tracer,指定 exporter 和 resource。
import "github.com/go-dev-frame/sponge/pkg/tracer"
func initTrace() {
// exporter := tracer.NewConsoleExporter() // 输出到终端
// exporter, f, err := tracer.NewFileExporter("trace.json") // 输出到文件
// exporter, err := tracer.NewJaegerExporter("http://localhost:14268/api/traces") // 输出到jaeger,使用collector http方式
exporter, err := tracer.NewJaegerAgentExporter("192.168.3.37", "6831") // 输出到jaeger,使用agent udp方式
resource := tracer.NewResource(
tracer.WithServiceName("your-service-name"), // 服务名称
tracer.WithEnvironment("dev"), // 环境标识
tracer.WithServiceVersion("demo"), // 服务版本
)
tracer.Init(exporter, resource) // 默认全量采集
// tracer.Init(exporter, resource, 0.5) // 按50%比例采集
}
在程序中创建 span,注意 ctx 需来自父级 span 的上下文传递。
_, span := otel.Tracer(serviceName).Start(
ctx,
spanName,
trace.WithAttributes(attribute.String("foo", "bar")), // 自定义属性
)
defer span.End()
// ......
单服务链路追踪示例
启动 Jaeger 与 Elasticsearch 服务
分布式追踪采用 Jaeger 实现链路追踪,Elasticsearch 作为存储。可通过 docker-compose 在本地启动这两个服务。
(1) Elasticsearch 服务
Elasticsearch 服务启动脚本,.env
文件包含 Elasticsearch 配置。启动服务:
docker-compose up -d
(2) Jaeger 服务
Jaeger 服务启动脚本,.env
文件包含 Jaeger 配置。启动服务:
docker-compose up -d
浏览器访问 Jaeger 查询首页 http://localhost:16686。
运行示例服务
使用 sponge 生成基于 SQL 的 Web 服务,解压代码后打开配置文件开启链路追踪功能,配置示例如下:
app:
enableTrace: true # 是否开启链路追踪,true: 开启,false: 关闭。开启时必须设置jaeger配置
tracingSamplingRate: 1.0 # 链路采样率,范围0~1.0浮点数。0表示不采样,1.0表示全量采样
jaeger:
agentHost: "127.0.0.1" # jaeger agent地址
agentPort: 6831 # jaeger agent端口
在终端执行命令运行服务:
# 生成swagger文档
make docs
# 编译并运行服务
make run
浏览器访问 http://localhost:8080/swagger/index.html,以GET请求为例,使用相同ID连续请求两次,分布式追踪效果如下图所示:

图中可见第一次请求包含4个span:
- 请求接口
/api/v1/teacher/1
- Redis 查询
- MySQL 查询
- Redis 缓存设置
说明首次请求会先查 Redis,未命中缓存后从 MySQL 获取数据,最后设置缓存。
第二次请求仅含2个 span:
- 请求接口
/api/v1/teacher/1
- Redis 查询
说明直接命中缓存,跳过了 MySQL 查询和缓存设置流程。
这些 span 都是自动生成的,但实际开发中常需要手动添加自定义 span,示例代码如下:
import "github.com/go-dev-frame/sponge/pkg/tracer"
tags := map[string]interface{}{"foo": "bar"}
_, span := tracer.NewSpan(ctx, "自定义span名称", tags)
defer span.End()
多服务链路追踪示例
以简化版电商微服务集群为例,源码参见此处。该集群包含shopgw
、product
、inventory
和comment
四个服务。分别修改这些服务的 YAML 配置(位于configs
目录),开启分布式追踪并配置Jaeger参数。
在product
、inventory
和comment
服务中,找到internal/service
目录下的模板文件,将panic("implement me")
替换为可正常运行的代码,并手动添加span和在代码中加入随机延迟。
依次启动shopgw
、product
、inventory
和comment
服务,浏览器访问 http://localhost:8080/apis/swagger/index.html 执行GET请求,分布式追踪界面如下图所示:

图中可见主trace共包含10个span:
- 请求/api/v1/detail接口
shopgw
服务调用product
的 grpc 客户端product
服务的 grpc 服务端product
服务中手动添加的 mockDAOshopgw
服务调用inventory
的grpc客户端inventory
服务的 grpc 服务端inventory
服务中手动添加的 mockDAOshopgw
服务调用comment
的 grpc 客户端comment
服务的 grpc 服务端comment
服务中手动添加的 mockDAO
shopgw
服务依次调用product
、inventory
和comment
服务获取数据。实际开发中可通过并行调用来节省时间,但需注意控制goroutine并发数量。