**
**
公共返回值
规范:
grpc接口 公共返回值:
type BaseResponse struct {
Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
Data *anypb.Any `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
}
使用 proto.google.protobuf 库 anypb.Any 结构体, 业务接口不需要关注 公共的结构体,eg: Code , Message, Data, 只要关注 返回的第三方业务数据
//改造前
func (a *Auth) GetUser(ctx context.Context, req *authsvr.User) (*authsvr.UserResponse, error) {
rsp := &authsvr.UserResponse{}
var user model.User
// where 条件要写在前面
result := mysql.DB().Where("username = ?", req.Username).Preload("Roles").First(&user)
if result.Error != nil {
log.Errorf(ctx, "GetUser:First err:%+v", result.Error)
rsp.Message = result.Error.Error()
rsp.Code = _const.DbOperationError
return rsp, nil
}
rsp.Data = &authsvr.User{
Id: uint64(user.ID),
Username: user.Username,
ChineseName: user.ChineseName,
Email: user.Email,
IsAdmin: user.IsAdmin,
}
// 用户角色
for _, role := range user.Roles {
rsp.Data.Roles = append(rsp.Data.Roles, role.Name)
}
rsp.Message = "ok"
rsp.Code = _const.Success
return rsp, nil
}
// 改造后
func (a *Auth) GetUser(ctx context.Context, req *authsvr.User) (*base.BaseResponse, error) {
var user model.User
// where 条件要写在前面
result := mysql.DB().Where("username = ?", req.Username).Preload("Roles").First(&user)
if result.Error != nil {
return response.Option().Code(_const.DbOperationError).Log(ctx, "GetUser:First err:%+v", result.Error).Add(func() {
log.Printf(ctx, "test")
}).End()
}
rsp := &authsvr.User{
Id: uint64(user.ID),
Username: user.Username,
ChineseName: user.ChineseName,
Email: user.Email,
IsAdmin: user.IsAdmin,
}
// 用户角色
for _, role := range user.Roles {
rsp.Roles = append(rsp.Roles, role.Name)
}
return response.Option().End(rsp)
}
// 客户端 模块调用
func GetUser(c *gin.Context) {
userName, _ := c.GetQuery("username")
rsp, err := handler.AuthClient.GetUser(context.Background(), &authsvr.User{
Username: userName,
})
handler.Response(c, rsp, err)
}
func Response(c *gin.Context, rsp *base.BaseResponse, err error) {
if rsp != nil {
// 业务逻辑正常返回
doResponseRsp(c, rsp)
} else if err != nil {
// Grpc内部服务异常,eg: 服务panic等
doResponseErr(c, err)
} else {
// 其他未知异常
c.JSON(http.StatusOK, SuccessResponse{
Code: _const.UnKnown,
Msg: _const.CodeMessageMap[_const.UnKnown],
})
}
}
func doResponseRsp(c *gin.Context, rsp *base.BaseResponse) {
if rsp.GetCode() != _const.Success {
c.JSON(http.StatusOK, SuccessResponse{
Code: int(rsp.Code),
Msg: rsp.Message,
})
} else {
rsptype, _ := rsp.Data.UnmarshalNew()
c.JSON(http.StatusOK, SuccessResponse{
Code: _const.Success,
Msg: "success",
Data: rsptype,
})
}
}
func doResponseErr(c *gin.Context, err error) {
c.JSON(http.StatusOK, SuccessResponse{
Code: _const.GRpcError,
Msg: err.Error(),
})
}
**
**
自定义GRPC插件
// 1、打印出入参数
// 2、log 错误打印会打印出RPC方法
// 3、自动填充 code,message,默认值是 10000,ok
// 4、当 returnRpcErrorOrNot 是true,如果但是code 不是 10000,则返回对应 message 对应的error
// 5、插件没有强依赖性,不加的话,上述1,2点不会生效,不会对程序造成任何影响
// 6、returnRpcErrorOrNot需要讨论,对于现有服务只是使用err 判断,如果不返回err 信息会影响原先的服务, 这种情况下,要把returnRpcErrorOrNot置为true, 或者改造原先服务,或者统一都返回err 信息
func UnaryRPCMethodCallbackInterceptor(returnRpcErrorOrNot bool) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
// 入参打印
log.Printf(ctx, "enter method:%s,req is: %v", info.FullMethod, req)
// context 增加返回头部
md := metadata.New(map[string]string{
METADATA_METHOD: info.FullMethod,
})
ctx = metadata.NewOutgoingContext(ctx, md)
rsp, err := handler(ctx, req)
// 出参打印
log.Printf(ctx, "leave method:%s,rsp is: %v,err is:%v", info.FullMethod, rsp, err)
if returnRpcErrorOrNot {
return rsp, err
}
return rsp, nil
}
}
type responseOption struct {
basersp *base.BaseResponse
ro responseOptions
err error
}
type responseOptions []func()
func Option() (r *responseOption) {
return &responseOption{
basersp: &base.BaseResponse{
Code: _const.Success,
Message: "ok",
},
ro: responseOptions{},
}
}
func (r *responseOption) Code(code int) *responseOption {
if code != _const.Success {
var message = ""
var ok bool
if message, ok = _const.CodeMessageMap[code]; !ok {
message = _const.CodeMessageMap[_const.UnKnown]
}
r.codeAndMessage(code, message)
}
return r
}
func (r *responseOption) codeAndMessage(code int, message string) *responseOption {
fn := func() {
r.basersp.Code = uint32(code)
r.basersp.Message = message
r.err = errors.New(message)
}
r.Add(fn)
return r
}
func (r *responseOption) Log(ctx context.Context, format string, args ...interface{}) *responseOption {
fn := func() {
md, _, ok := metadata.FromOutgoingContextRaw(ctx)
if ok {
// 增加打印method
format = fmt.Sprintf("method:%s ,"+format, md[METADATA_METHOD])
}
if r.basersp.Code == _const.Success {
log.Infof(ctx, format, args)
} else {
log.Errorf(ctx, format, args)
}
}
r.Add(fn)
return r
}
func (r *responseOption) Add(fns ...func()) *responseOption {
r.ro = append(r.ro, fns...)
return r
}
// 返回返回值
func (r *responseOption) End(pbTypes ...proto.Message) (*base.BaseResponse, error) {
for _, fn := range r.ro {
fn()
}
// 默认取第一个入参
for _, pbType := range pbTypes {
if pbType != (proto.Message)(nil) {
pb, err := anypb.New(pbType)
if err != nil {
// 特定的返回Code
r.basersp.Code = _const.RpcMethodDefinitionPlugeError
r.basersp.Message = _const.CodeMessageMap[int(r.basersp.Code)]
r.err = err
} else {
r.basersp.Data = pb
}
}
break
}
return r.basersp, r.err
}
// 使用
sb := zgrpc.NewServerBuilder()
sb.WithServerOptions(grpc.ChainUnaryInterceptor(response.UnaryRPCMethodCallbackInterceptor(false))).WithRegister(authsvr.RegisterAuthServer, new(handler.Auth))