原创分享 go-kit 微服务 使用 GRPC (并为每个请求添加 UUID)

hwholiday · 2020年02月12日 · 132 次阅读

go-kit 微服务 使用 Grpc(并传递请求 ID)

grpc

  • gRPC 是一个高性能、通用的开源 RPC 框架,其由 Google 主要面向移动应用开发并基于 HTTP/2 协议标准而设计,基于 ProtoBuf(Protocol Buffers) 序列化协议开发
  • grpc 使用 案例

简介

  • 通过 grpc 实现一个用户中心实现简单的鉴权中心并返回用户 token
  • 上篇文章《go-kit 微服务 身份认证(JWT)》已经实现的一个 http 的鉴权中心,所以我们只需要改变 transport 层逻辑
  • 编写 ProtoBuf
//service.proto
syntax = "proto3";
package pb;
import "user.proto";
service User {
    rpc RpcUserLogin (Login) returns (LoginAck) {
    }
}
//user.proto
message Login {
    string Account = 1;
    string Password = 2;
}
message LoginAck {
    string Token = 1;
}
  • 编译 ProtoBuf
protoc --go_out=plugins=grpc:. *.proto

transport 层修改

  • 定义 grpcServer 结构体
type grpcServer struct {
    login grpctransport.Handler
}
  • 实现 RpcUserLogin 接口
func (s *grpcServer) RpcUserLogin(ctx context.Context, req *pb.Login) (*pb.LoginAck, error) {
    _, rep, err := s.login.ServeGRPC(ctx, req)
    if err != nil {
        return nil, err
    }
    return rep.(*pb.LoginAck), nil
}
  • 将 endpoint 的方法加载到 grpcServer 对象中
func NewGRPCServer(endpoint v5_endpoint.EndPointServer, log *zap.Logger) pb.UserServer {
    options := []grpctransport.ServerOption{
        grpctransport.ServerBefore(func(ctx context.Context, md metadata.MD) context.Context {
            ctx = context.WithValue(ctx, v5_service.ContextReqUUid, md.Get(v5_service.ContextReqUUid))
            return ctx
        }),
        grpctransport.ServerErrorHandler(NewZapLogErrorHandler(log)),
    }
    return &grpcServer{login: grpctransport.NewServer(
        endpoint.LoginEndPoint,
        RequestGrpcLogin,
        ResponseGrpcLogin,
        options...,
    )}
}

修改 main 方法

utils.NewLoggerServer()
golangLimit := rate.NewLimiter(10, 1)
server := v5_service.NewService(utils.GetLogger())
endpoints := v5_endpoint.NewEndPointServer(server, utils.GetLogger(), golangLimit)
grpcServer := v5_transport.NewGRPCServer(endpoints, utils.GetLogger())
utils.GetLogger().Info("server run :8881")
grpcListener, err := net.Listen("tcp", ":8881")
if err != nil {
    utils.GetLogger().Warn("Listen", zap.Error(err))
    os.Exit(0)
}
baseServer := grpc.NewServer(grpc.UnaryInterceptor(grpctransport.Interceptor))
pb.RegisterUserServer(baseServer, grpcServer)
if err = baseServer.Serve(grpcListener); err != nil {
    utils.GetLogger().Warn("Serve", zap.Error(err))
    os.Exit(0)
}

编写客户端

  • go-kit 客户端
func NewGRPCClient(conn *grpc.ClientConn, log *zap.Logger) v5_service.Service {
    options := []grpctransport.ClientOption{
        grpctransport.ClientBefore(func(ctx context.Context, md *metadata.MD) context.Context {
            UUID := uuid.NewV5(uuid.Must(uuid.NewV4()), "req_uuid").String()
            log.Debug("给请求添加uuid", zap.Any("UUID", UUID))
            md.Set(v5_service.ContextReqUUid, UUID)
            ctx = metadata.NewOutgoingContext(context.Background(), *md)
            return ctx
        }),
    }
    var loginEndpoint endpoint.Endpoint
    {
        loginEndpoint = grpctransport.NewClient(
            conn,
            "pb.User",
            "RpcUserLogin",
            RequestLogin,
            ResponseLogin,
            pb.LoginAck{},
            options...).Endpoint()
    }
    return v5_endpoint.EndPointServer{
        LoginEndPoint: loginEndpoint,
    }
}
func RequestLogin(_ context.Context, request interface{}) (interface{}, error) {
    req := request.(*pb.Login)
    return &pb.Login{Account: req.Account, Password: req.Password}, nil
}
func ResponseLogin(_ context.Context, response interface{}) (interface{}, error) {
    resp := response.(*pb.LoginAck)
    return &pb.LoginAck{Token: resp.Token}, nil
}
  • go-kit 客户端 调用方法
logger := logtool.NewLogger(
        logtool.SetAppName("go-kit"),
        logtool.SetDevelopment(true),
        logtool.SetLevel(zap.DebugLevel),
    )
    conn, err := grpc.Dial("127.0.0.1:8881", grpc.WithInsecure())
    if err != nil {
        t.Error(err)
        return
    }
    defer conn.Close()
    svr := NewGRPCClient(conn, logger)
    ack, err := svr.Login(context.Background(), &pb.Login{
        Account:  "hwholiday",
        Password: "123456",
    })
    if err != nil {
        t.Error(err)
        return
    }
    t.Log(ack.Token)
  • grpc 原生客户端
serviceAddress := "127.0.0.1:8881"
conn, err := grpc.Dial(serviceAddress, grpc.WithInsecure())
if err != nil {
    panic("connect error")
}
defer conn.Close()
userClient := pb.NewUserClient(conn)
UUID := uuid.NewV5(uuid.Must(uuid.NewV4()), "req_uuid").String()
md := metadata.Pairs( v5_service.ContextReqUUid, UUID)
ctx := metadata.NewOutgoingContext(context.Background(), md)
res, err := userClient.RpcUserLogin(ctx, &pb.Login{
    Account:  "hw",
    Password: "123",
})
if err != nil {
    t.Error(err)
    return
}
t.Log(res.Token)

传递客户端请求 ID 到服务端代码

  • 客户端
......
UUID := uuid.NewV5(uuid.Must(uuid.NewV4()), "req_uuid").String()
log.Debug("给请求添加uuid", zap.Any("UUID", UUID))
md.Set(v5_service.ContextReqUUid, UUID)
ctx = metadata.NewOutgoingContext(context.Background(), *md)
......
  • 服务端
......
ctx = context.WithValue(ctx, v5_service.ContextReqUUid, md.Get(v5_service.ContextReqUUid))
......

运行日志


//客户端
2020-01-07 15:42:24 INFO    logtool/log.go:89   [NewLogger] success
2020-01-07 15:42:24 DEBUG   client/client.go:20 给请求添加uuid {"UUID": "a8360f58-6f0d-588f-83c9-b3dc00fe60f6"}
--- PASS: TestGrpcClient (0.00s)
    grpc_test.go:36: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwiZXhwIjoxNTc4MzgyOTc0LCJpYXQiOjE1NzgzODI5NDQsImlzcyI6ImtpdF92NCIsIm5iZiI6MTU3ODM4Mjk0NCwic3ViIjoibG9naW4ifQ.PSd1mWjfePR0IP3cw8gF9yN3IaNQDt9TaDpSk4QzUDc
PASS


//服务端
2020-01-07 15:42:16     INFO    logtool/log.go:89       [NewLogger] success
2020-01-07 15:42:16     INFO    v5_user/main.go:23      server run :8881
2020-01-07 15:42:24     DEBUG   v5_service/service.go:28        [a8360f58-6f0d-588f-83c9-b3dc00fe60f6]  {"调用 v5_service rvice": "Login 处理请求"}
2020-01-07 15:42:24     DEBUG   v5_service/service.go:35        [a8360f58-6f0d-588f-83c9-b3dc00fe60f6]  {"调用 v5_service rvice": "Login 处理请求", "处理返回值": "Token:\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6joxNTc4MzgyOTc0LCJpYXQiOjE1NzgzODI5NDQsImlzcyI6ImtpdF92NCIsIm5iZiI6MTU3ODM4Mjk0NCwic3ViIjoibG9naW4ifQ.PSd1mWjfePR0IP3cw8gF9yN3IaNQDt9TaDpSk4QzUDc\" "}
2020-01-07 15:42:24     DEBUG   v5_service/middleware.go:31     [a8360f58-6f0d-588f-83c9-b3dc00fe60f6]  {"调用 Login logMilewareServer": "Login", "req": "Account:\"hwholiday\" Password:\"123456\" ", "res": "Token:\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiaHdob2xpZGF5IiwiRGNJZCI6MSwiZXhwIjoxNTc4MzgyOTc0LCJpYXQiOjE1NzgzODI5NDQsImlzcyI6ImtpdF92NCIsIm5iZiI6MTU3ODM4Mjk0NCwic3ViIjoibG9naW4ifQ.PSd1mWjfePR0IP3cw8gF9yN3IaNQDt9TaDpSk4QzUDc\" ", "err": null}
2020-01-07 15:42:24     DEBUG   v5_endpoint/middleware.go:18    [a8360f58-6f0d-588f-83c9-b3dc00fe60f6]  {"调用 v4_endpointoggingMiddleware": "处理完请求", "耗时毫秒": 0}


结语

  • 这里只实现了一个煎蛋的 grpc 在 go-kit 的使用场景
  • 欢迎添加 QQ 一起讨论

完整代码地址

联系 QQ: 3355168235

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册