参考文档
https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/examples/client/main.go
https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/examples/server/main.go
https://github.com/open-telemetry/opentelemetry-go/blob/main/example/jaeger/main.go
直接展示代码:
client代码:
package mainimport ("context""fmt""go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"myInterceptor "go_grpc/grpc_middleware/interceptor""go_grpc/grpc_middleware/model""go_grpc/grpc_middleware/pb""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure""google.golang.org/grpc/metadata""log""os""time"
)func main() {//创建traceProvidertp, err := myInterceptor.NewTracerProvider("http://localhost:14268/api/traces", "grpc_mid_client")if err != nil {fmt.Println("NewTracerProvider err:", err)os.Exit(1)}conn, err := grpc.Dial("localhost:4399", grpc.WithTransportCredentials(insecure.NewCredentials()),//一元拦截器grpc.WithChainUnaryInterceptor(//openTelemetry 链路追踪otelgrpc.UnaryClientInterceptor(otelgrpc.WithTracerProvider(tp)),),//流拦截器grpc.WithChainStreamInterceptor(func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {// Pre-processing logics := time.Now()cs, err := streamer(ctx, desc, cc, method, opts...)// Post processing logiclog.Printf("method: %s, latency: %s\n", method, time.Now().Sub(s))return cs, err}),)//必须执行这一步,才能形成正常的链路追踪defer func() {println("关闭TracerProvider。所有注册的跨度处理器都会按照它们注册的顺序关闭,并释放所有持有的计算资源。")if err := tp.Shutdown(context.Background()); err != nil {panic(err)}}()if err != nil {log.Fatalf("connection failed,err:%s", err)}client := pb.NewOrderClient(conn)ctx := metadata.NewOutgoingContext(context.Background(), md)//客户端发送resp, err := client.OrderDetail(ctx, &pb.OrderReq{OrderId: 5,})if err != nil {log.Fatalf("orderDetail failed,err:%s", err)}fmt.Printf("resp:%v\n", resp)}
server代码(这里只展示核心代码):
//创建traceProvidertp, err := myInterceptor.NewTracerProvider("http://localhost:14268/api/traces", "grpc_mid_server")if err != nil {fmt.Println("NewTracerProvider err:", err)os.Exit(1)}orderServer := service.NewOrderService()rpcServer := grpc.NewServer(//4.引入grpc-middleware定义的拦截器grpc.ChainUnaryInterceptor(//openTelemetry 链路追踪otelgrpc.UnaryServerInterceptor(otelgrpc.WithTracerProvider(tp)),),)pb.RegisterOrderServer(rpcServer, orderServer)
myInterceptor包:
package interceptorimport ("go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/propagation""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
)// NewTracerProvider 创建trace的提供者
// tracerProvider returns an OpenTelemetry TracerProvider configured to use
// the Jaeger exporter that will send spans to the provided url. The returned
// TracerProvider will also use a Resource configured with all the information
// about the application.
// 参考gitHub example :https://github.com/open-telemetry/opentelemetry-go/blob/main/example/jaeger/main.go
// 访问 http://127.0.0.1:16686/ 即可通过jaeger查看调用过程
func NewTracerProvider(url string, service string) (*tracesdk.TracerProvider, error) {// Create the Jaeger exporter// 创建 Jaeger exporterexp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))if err != nil {return nil, err}tp := tracesdk.NewTracerProvider(// Always be sure to batch in production.tracesdk.WithBatcher(exp),// Record information about this application in a Resource.tracesdk.WithResource(resource.NewWithAttributes(semconv.SchemaURL,semconv.ServiceNameKey.String(service),attribute.String("environment", "dev"),attribute.Int64("ID", 1),)),)//SetTracerProvider将“tp”注册为全局跟踪提供程序。otel.SetTracerProvider(tp)//传播(Propagation)是在服务和进程之间传递上下文的机制。它对上下文对象进行序列化或反序列化,并提供相关的跟踪(Trace)信息,以便从一个服务传播到另一个服务。otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))return tp, nil
}
代码写好后,先启动server端的代码,再启动client端的代码。然后登录http://127.0.0.1:16686/ 即可通过jaeger查看调用过程。(要先下载jaeger)
【如何看客户端的trace信息有没有传递到服务端?】
可以在server端打印上下文的入站metadata:
md, ok := metadata.FromIncomingContext(ctx) //如果客户端那边有开启链路追踪,这里就能输出:traceparent:[00-c92465d487349809e1c1157ba4133f77-0aa3bcd98ed5fedb-01]
fmt.Printf("md:%v\n", md)
可以看到输出metadata携带了trace信息,说明客户端的trace顺利传递到服务端了: