当前位置: 首页 > news >正文

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

存在问题

grpc 调用问题

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

{
    "code": 20000,
    "msg": "Success",
    "data": {}
}

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

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

如果每次都定义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,
	}

日志频繁记录问题

存在问题:

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

代码示例如下:

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

与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,
	}
}

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
		}
	}
}

日志记录中间件

// 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
		}
	}
}

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

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

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

if err != nil {
		return res.FailDefaultResponse("server err"), errors.Errorf("server err err. param:%v", param)
}

相关文章:

  • 求求你们了,别再乱用 parallelStream 了,速度竟然比 Stream 还要慢!!
  • 【机器学习】算法改进——小批量和软更新
  • JMX概念及实际开发应用【实现IP黑名单】
  • flask-sqlalchemy连接数据库
  • 在设计测试用例前你应该考虑的重点在哪里?
  • 5.Nodejs中的包、npm、第三方模块、package.json以及cnpm
  • 如何使用Google Analytics跟踪WordPress网站的用户参与度
  • 大型医院his系统源码 医院信息管理系统源码 C/S架构
  • EN 16069建筑物用隔热产品.工厂制造的聚乙烯泡沫(PEF)产品—CE认证
  • UE4 源码解析----引擎初始化流程
  • 叶酸PEG衍生物​DBCO-PEG-Folate,DBCO-PEG-FA,二苯基环辛炔-聚乙二醇-叶酸
  • 【dll】windows下使用vs编译动态链接库dll与使用
  • 2022年ios证书最新申请流程
  • 普冉 PY32F003 资料和入坑方法
  • springboot+微信小程序健康饮食系统毕业设计源码280920
  • java中具有继承关系的类及其对象初始化顺序
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • Making An Indicator With Pure CSS
  • PHP变量
  • python学习笔记 - ThreadLocal
  • Selenium实战教程系列(二)---元素定位
  • windows下使用nginx调试简介
  • 当SetTimeout遇到了字符串
  • 搞机器学习要哪些技能
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 使用putty远程连接linux
  • 探索 JS 中的模块化
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 一文看透浏览器架构
  • 移动端 h5开发相关内容总结(三)
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • ​Java并发新构件之Exchanger
  • # 数据结构
  • #每日一题合集#牛客JZ23-JZ33
  • #每天一道面试题# 什么是MySQL的回表查询
  • $.each()与$(selector).each()
  • (BFS)hdoj2377-Bus Pass
  • (day6) 319. 灯泡开关
  • (安卓)跳转应用市场APP详情页的方式
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (南京观海微电子)——COF介绍
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • .Mobi域名介绍
  • .net 4.0发布后不能正常显示图片问题
  • .NET HttpWebRequest、WebClient、HttpClient
  • .NET Reactor简单使用教程
  • .net Signalr 使用笔记
  • .net 发送邮件
  • .Net6使用WebSocket与前端进行通信
  • .NET导入Excel数据
  • .net中我喜欢的两种验证码
  • @private @protected @public