• go-kit grpc调用及中间件封装


    存在问题

    grpc 调用问题

    通常我们向业务返回会定义如下的结构:

    {
        "code": 20000,
        "msg": "Success",
        "data": {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是如果我们定义如下的proro,grpc的返回值可以在客户端不能直接使用,还需要使用json进行解析

    message Response {
      string code = 1;  // 响应码
      string msg = 2;  // 响应描述信息
      string data = 3;  // json 格式待定
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果每次都定义rpc的响应的话,又会引发另一个问题:

    每个rpc请求都会定义一个 Response ,这样我们的 data 的数据类型可能是不同的。

    如果我们在写service的时候,即需要处理error请求,返回error的结果,还需要返回正常的结构,那么我们的代码中可能会经常出现如下的现象:

    一个 func 可能出现返回多个返参,会使得代码变得臃肿。

    if err != nil {
    		logger.Warnf("方法名: %s, 错误: %+v, 参数:  %s", methodName, err, url)
    		return &pb.Response{
    			Code: 32000,
    			Msg:  err.Error(),
    			Data: nil,
    		}
    	}
    
    	if err != nil {
    		logger.Warnf("方法名: %s, 错误: %+v, 参数:  %+v", methodName, err, cond2)
    		return &pb.Response{
    			Code: 32001,
    			Msg:  err.Error(),
    			Data: nil,
    		}
    	}
    
    	return &pb.Response{
    		Code: 20000,
    		Msg:  err.Error(),
    		Data: nil,
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    日志频繁记录问题

    存在问题:

    • 项目中service遇到error经常会写一些error/warn日志
    • 很多 func 定义了 error 但是返回的却是 nil因为已经记录了日志,没必要往下继续执行

    代码示例如下:

    if err != nil {
    	logger.Warnf("方法名: %s, 错误: %+v, 参数:  %+v", methodName, err, cond2)
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4

    与go-kit的分层理念并不符合,service专注于业务的开发。

    grpc异常panic导致程序挂掉

    如果在一次grpc调用中,出现了 panic 异常,没有 recover 处理的话,会导致程序宕机。

    封装通用结构体和响应

    type CommonResponse struct {
    	Code string      `json:"code"`
    	Msg  string      `json:"msg"`
    	Data interface{} `json:"data"`
    }
    
    // SuccessResponse 成功的响应
    func SuccessResponse(data interface{}) *CommonResponse {
    	return &CommonResponse{
    		Code: SUCCESS_CODE,
    		Msg:  SUCCESS_MSG,
    		Data: data,
    	}
    }
    
    // OkResponse 成功的响应
    func OkResponse() *CommonResponse {
    	return &CommonResponse{
    		Code: SUCCESS_CODE,
    		Msg:  SUCCESS_MSG,
    	}
    }
    
    // FailDefaultResponse 失败的响应
    func FailDefaultResponse(msg string) *CommonResponse {
    	return &CommonResponse{
    		Code: ERROR_CODE,
    		Msg:  msg,
    	}
    }
    
    // FailResponse 失败的响应
    func FailResponse(code, msg string) *CommonResponse {
    	return &CommonResponse{
    		Code: code,
    		Msg:  msg,
    	}
    }
    
    // FailDefaultResponseWithData 失败的响应
    func FailDefaultResponseWithData(msg string, failData interface{}) *CommonResponse {
    	return &CommonResponse{
    		Code: ERROR_CODE,
    		Msg:  msg,
    		Data: failData,
    	}
    }
    
    // FailResponseWithData 失败的响应
    func FailResponseWithData(code, msg string, failData interface{}) *CommonResponse {
    	return &CommonResponse{
    		Code: code,
    		Msg:  msg,
    		Data: failData,
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    go-kit中间件封装

    go-kit可以封装中间件,在 endpoint 层进行绑定,类似于java的AOP思想,可以用来日志记录、监控、统计等场景。

    Recover中间件

    封装recover中间件,绑定到每个rpc service,处理 panic 异常,解决程序宕机问题。

    // RecoveringEndpointMiddleware panic 处理
    func RecoveringEndpointMiddleware(method string) endpoint.Middleware {
    	return func(next endpoint.Endpoint) endpoint.Endpoint {
    		return func(ctx context.Context, request interface{}) (response interface{}, e error) {
    
    			defer func() {
    				if err := recover(); err != nil {
    					switch err.(type) {
    					case runtime.Error: // 运行时错误
    						logger.Errorf("method:%v runtime error:%+v", method, err)
    					default: // 非运行时错误
    						logger.Errorf("method:%v error::%+v", method, err)
    					}
    					debug.PrintStack()
    					
    					// 自定义失败的响应
    					response = res.FailResponse(res.ERROR_CODE, res.ERROR_MSG)
    					e = nil
    				}
    			}()
    
    			response, e = next(ctx, request)
    			return response, e
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    日志记录中间件

    // LoggingMiddleware returns an endpoint middleware that logs the
    // duration of each invocation, and the resulting error, if any.
    func LoggingMiddleware(method string) endpoint.Middleware {
    	return func(next endpoint.Endpoint) endpoint.Endpoint {
    		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
    			r, errs := next(ctx, request)
    
    			defer func(begin time.Time) {
    				if errs != nil {
    					logger.Errorf("method: %v,time consuming:%v err: %v", method, time.Since(begin), errs.Error())
    				} else {
    					logger.Infof("method: %v,time consuming:%v", method, time.Since(begin))
    				}
    
    				errs = nil
    			}(time.Now())
    
    			return r, errs
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    next 函数可以返回上一层的error,service的error在这里统一处理,然后在这里统一进行日志的记录。

    记录日志时,并且记录service的执行时间。

    如果遇到业务异常,直接自定义再次处理error:

    if err != nil {
    		return res.FailDefaultResponse("server err"), errors.Errorf("server err err. param:%v", param)
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    -绝对质数-
    [附源码]计算机毕业设计springboot动物保护协会网站
    某红书旋转滑块验证码分析与协议算法实现(高通过率)
    NoSuchMethodError
    K8S访问控制------认证(authentication )、授权(authorization )体系
    Lambda 表达式
    阅读mybatis的源码的思路
    基于JAVA医院临床管理系统录屏计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    【《高性能 MySQL》笔记】性能优化
    MATLAB算法实战应用案例精讲-【人工智能】ROS机器人(最终篇)
  • 原文地址:https://blog.csdn.net/qq_42937522/article/details/126661736