grpc内置了客户端和服务端的请求追踪,基于golang.org/x/net/trace包实现,默认是开启状态,可以查看事件和请求日志,对于基本的请求状态查看调试也是很有帮助的。

客户端与服务端基本一致,这里以服务端开启trace server为例,修改hello项目服务端代码:

代码示例

package main

import (
    "context"
    "fmt"
    "go_study/framework/grpc/pb" // 导入proto
    "golang.org/x/net/trace"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/grpclog"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
    "net"
    "net/http"
)

// 定义 UserInfoService 服务
type UserInfoService struct{
    pb.UnimplementedUserInfoServiceServer
}

// GetUserInfo 实现方法
func (s *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, err error) {
    //err = tokenAuth(ctx)
    //if err != nil {
    //    return nil, err
    //}
    // 通过用户名查询用户信息
    name := req.Name
    // 数据里查用户信息
    if name == "zs" {
        resp = &pb.UserResponse{
            Id:    1,
            Name:  name,
            Age:   22,
            Hobby: []string{"Sing", "Run"},
        }
    }
    return
}

type HelloService struct {
    pb.UnimplementedHelloServiceServer
}

func (h *HelloService) SayHello(ctx context.Context, in *pb.HelloRequest) (resp *pb.HelloResponse, err error) {
    //err = tokenAuth(ctx)
    //if err != nil {
    //    return nil, err
    //}
    resp = new(pb.HelloResponse)
    resp.Result  = fmt.Sprintf("hello %s", in.Name)
    return resp, nil
}

// 使用这种方式验证token 每个服务端每个服务中都验证一次,比较繁琐
func tokenAuth(ctx context.Context) error {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return status.Errorf(codes.Unauthenticated, "无Token认证信息")
    }

    var (
        appid  string
        appkey string
    )

    if val, ok := md["appid"]; ok {
        appid = val[0]
    }

    if val, ok := md["appkey"]; ok {
        appkey = val[0]
    }

    if appid != "101010" || appkey != "i am key" {
        return status.Errorf(codes.Unauthenticated, "Token认证信息无效: appid=%s, appkey=%s", appid, appkey)
    }
    return nil
}

// interceptor 拦截器
// grpc.UnaryServerInfo 包含有关服务器端一元 RPC 的各种信息。所有 per-rpc 信息都可能被拦截器改变
func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    err := tokenAuth(ctx)
    if err != nil {
        return nil, err
    }
    // 继续处理请求
    return handler(ctx, req)
}

func init(){
    // 新版 grpc 开启 Trace 需要这个
    grpc.EnableTracing = true
}


func main(){
    // 地址
    addr := "127.0.0.1:8180"
    // 1.监听
    listener, err := net.Listen("tcp", addr)
    if err != nil {
        grpclog.Fatalf("Failed to listen: %v", err)
    }
    fmt.Printf("监听端口:%s\n", addr)

    // tls认证
    creds,err := credentials.NewServerTLSFromFile("./framework/grpc/keys/server.pem", "./framework/grpc/keys/server.key")
    if err != nil {
        grpclog.Fatalf("Failed to generate credentials %v", err)
    }

    var opts []grpc.ServerOption

    // 添加 tls 认证
    opts = append(opts, grpc.Creds(creds))

    // 注册拦截器
    opts = append(opts, grpc.UnaryInterceptor(interceptor))

    // 2.实例化gRPC
    s := grpc.NewServer(opts...)

    // 3.在gRPC上注册微服务
    var u = UserInfoService{}
    pb.RegisterUserInfoServiceServer(s, &u)

    var h = HelloService{}
    pb.RegisterHelloServiceServer(s, &h)

    grpclog.Infoln("Listen on " + addr + " with TLS")

    // 开启grpc的trace
    go startTrace()

    // 4.启动服务端
    s.Serve(listener)
}

func startTrace() {
    trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
        return true, true
    }
    go http.ListenAndServe(":50051", nil)
    grpclog.Infoln("Trace listen on 50051")
}

注意:新版 grpc 需要开启 grpc.EnableTracing = true,否则只会看到,显示地址的空白。

服务端事件查看

访问:localhost:50051/debug/events,结果如图:

可以看到 服务端注册的服务 和 服务正常启动 的 事件信息。

请求日志信息查看

访问:localhost:50051/debug/requests,结果如图:

作者:joker.liu  创建时间:2023-05-23 15:15
最后编辑:joker.liu  更新时间:2023-05-23 15:45