链路追踪的集成与使用

介绍

链路追踪Tracing Analysis为分布式应用的开发者提供了完整的调用链路还原、调用请求量统计、链路拓扑、应用依赖分析等工具,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提高微服务时代下的开发诊断效率。  
```  
  
## 集成到go-zero系统(以product api服务和product rpc服务举例)  
  
1.在各个服务的配置文件增加【链路追踪配置】:  
```yaml  
Name: product  
Host: 0.0.0.0  
Port: 6511  
ProductRPC:  
  Endpoints:    - 127.0.0.1:7511  Timeout: 0  NonBlock: true# jwtAuth  
JwtAuth:  
  AccessSecret: 3d0f2052dede4b44caff1f643d0a4e1f  AccessExpire: 86400  
# 缓存  
Cache:  
  - Host: 192.168.1.168:6379    Pass:# 链路追踪配置  
Telemetry:  
  Name: product  Endpoint: http://192.168.1.53:14268/api/traces  Sampler: 1.0  Batcher: jaeger  
```  
2.修改config.go  
  
```go  
  
package config  
  
import (  
   "github.com/zeromicro/go-zero/core/stores/cache"   "github.com/zeromicro/go-zero/core/trace" //新增  
   "github.com/zeromicro/go-zero/zrpc")  
  
type Config struct {  
   zrpc.RpcServerConf   Mysql struct {      DataSource string   }   Cache           cache.CacheConf   MqRpcConf       zrpc.RpcClientConf   ChainRpcConf    zrpc.RpcClientConf   CheckoutRpcConf zrpc.RpcClientConf   CommonRpcConf   zrpc.RpcClientConf   Order           struct {      PayingTimeout uint64   }   Telemetry trace.Config //新增  
}  
  
  
```  
## span和trace介绍  
为什么能够进行整条链路的追踪? 其实就是一个trace id将一连串的span信息连起来了。根据Span记录的信息再进行整合就可以获取整条链路的信息。下面是链路追踪的核心概念:  
  
span:   
```text  
基本的工作单元,每次发送一个远程调用服务就会产生一个 Span。  
Span 是一个 64 位的唯一 ID。  
通过计算 Span 的开始和结束时间,就可以统计每个服务调用所花费的时间。  
```  
trace:  
  
```text  
每次客户端访问微服务系统的 API 接口,可能中间会调用多个微服务,  
每次调用都会产生一个新的 Span,而多个 Span 组成了 Trace  
```  
  
  
  
  
## 使用  
span:  
系统默认情况下会自动记录api和rpc服务请求的链路,如图所示:  
  
<img src="https://wakie.oss-cn-beijing.aliyuncs.com/screenshot-20220825-142839.png">  
  
1.从context中获取span: 

```go  
span := tracking.SpanFromContext(l.ctx)  
```  
  
  
2.设置span的属性:  
  
```go  
tracking.SetAttributes(span, attribute.KeyValue{Key: "key", Value: attribute.StringValue("value")})  
```  
  
3.设置span状态:  
```go  
span.SetStatus(codes.Error, "描述信息")  
```  
4.记录错误:  
```go  
span.RecordError(errors.New("这是一个错误"))  
```  
  
5.添加日志:  
  
```go  
tracking.AddEvent(span, "名称", trace.WithAttributes(  
    attribute.KeyValue{Key: "k1", Value: attribute.StringValue("v1")},    attribute.KeyValue{Key: "k2", Value: attribute.StringValue("v2")},    attribute.KeyValue{Key: "k3", Value: attribute.StringValue("v3")},    attribute.KeyValue{Key: "k4", Value: attribute.StringValue("v4")},))  
```  
  
6.新建span:  
当需要记录新函数调用链的时候用到,首选需要传入当前context到该函数中:  
  
```go  
music, err := l.svcCtx.CustomProductModel.GetMusicMusic(l.ctx, in)  
if err != nil {  
    return nil, xerr.NewErrCode(xerr.DbError, err)}  
```  

然后在该函数中就记录使用上述步骤,但这时候获取span的方式不同应使用NewSpanFromContext:  

```go  
func (m *CustomProductModel) GetMusicMusic(ctx context.Context, in *pb.ProductPageReq) (*pb.MusicListResp, error) {  
    _, span := tracking.NewSpanFromContext(ctx, "GetMusicMusic")    span.SetAttributes(attribute.KeyValue{Key: "dddd", Value: attribute.StringValue("2233423423")})    defer span.End()}  
```  

如果不再需要有新增子链路则需要调用 defer span.End()  
  
  
tracking包为自定义span方法的扩展包,调用这里面的方法会自动加上caller

## jaeger 安装  
  
```shell  
docker run -d --name jaeger \  
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \  
-e COLLECTOR_OTLP_ENABLED=true \  
-p 6831:6831/udp \  
-p 6832:6832/udp \  
-p 5778:5778 \  
-p 16686:16686 \  
-p 4317:4317 \  
-p 4318:4318 \  
-p 14250:14250 \  
-p 14268:14268 \  
-p 14269:14269 \  
-p 9411:9411 \  
jaegertracing/all-in-one:1.37

16686 为UI访问端口

{"TransactionNo":"002112022082311461310407997136606896128","Amount":0.01,"AppId":"app_3e5d214b-dff9-42de-a347-1796916e7c97","Reason":"测试退款"}