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

2023-11-06

存在问题

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)
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go-kit grpc调用及中间件封装 的相关文章

  • golang导入结构体指针

    好的 我有一个主包和一个 http 处理程序包 本质上我想做的是设置一个全局结构 这样我就可以随时调用该结构中的信息 我尝试的示例的基本概要如下 主包导入处理函数 主包调用handlerfunc Handlerfunc 将 http Res
  • 将 []string 传递给需要可变参数的函数

    为了不一遍又一遍地重复我的自我 我想创建一个处理运行一些命令的函数 func runCommand name string arg string error cmd exec Command name arg if err cmd Run
  • Bash脚本无法执行Go命令

    我正在尝试编写一个 bash 脚本来自动在不同的目录中运行 go get install 相关部分在这里 cd web go get cd web go install cd services go get cd services go i
  • Google App Engine Golang 没有这样的文件或目录

    我正在用 Go 开发一个 Google App Engine 项目 并陷入了读取文件的困境 事实上 应用程序在本地运行得很好 然而 部署时 它会恐慌告诉我没有这样的文件或目录 这是我的 fileValue 方法 func fileValue
  • runtime.LockOSThread 是否允许子 goroutine 在同一个操作系统线程中运行?

    我明白在 Go 中 runtime LockOSThread https golang org pkg runtime LockOSThread将一个 goroutine 绑定到一个操作系统线程 并且不允许其他 goroutine 在该线程
  • Golang、mysql:错误1040:连接过多

    我正在使用 github com go sql driver mysql 驱动程序 我打开一个数据库 db err sql Open mysql str 然后我有两个函数 每个函数被调用 200 次 并使用以下 mysql 代码 rows
  • Bazel 构建缺少严格的依赖关系

    我正在尝试使用 brazel 构建 Go 应用程序 它是一个现有的私有 GitHub 存储库 位置如下 github xyz com repo name 我正在研究 我的目标是从 main go 文件创建一个二进制文件 该文件的方法依赖于其
  • Golang Appengine 项目无法构建

    我有一个使用 golang 的应用程序引擎项目 我已经大约一年没有碰过了 我现在无法让它在之前构建的机器上构建 我收到以下错误 go app builder 解析输入失败 解析器 src golang org x net internal
  • 为什么我的 SQL 占位符没有被替换(使用 Go pq)?

    根据文档 我正在这样做 var thingname string asdf var id int err database QueryRow SELECT id from things where thing thingname Scan
  • 视频第一帧

    我正在创建一个单页应用程序 后端使用 Golang 前端使用 javascript 我想找到一种使用 Golang 获取视频第一帧的方法 首先 我将 mp4 视频文件上传到服务器 它保存在服务器上 有没有办法使用 Golang 获取该视频的
  • Golang 基础知识 struct 和 new() 关键字

    我正在学习 golang 当我阅读描述结构的章节时 我遇到了初始化结构的不同方法 p1 passport var p2 passport p3 passport Photo make byte 0 0 Name Scott Surname
  • 投射回更专业的界面

    我正在用 Go 编写一个游戏 在 C 中 我将所有实体类存储在 BaseEntity 类的数组中 如果一个实体需要在世界中移动 那么它将是一个从 BaseEntity 派生的 PhysEntity 但添加了方法 我尝试模仿这是 go pac
  • 如何在C#中执行Go函数

    有没有办法从 C 执行 Go 函数 例如 对于 Python 我会使用 Ironpython 我知道我可以生成一个进程来执行 Go 脚本 但如果可能的话 我真的不想回退到这样的解决方案 Google 搜索没有显示任何内容 那么有什么方法可以
  • 完全删除使用“go get”安装的软件包?

    我正在使用 Go 1 13 1 最新版本 我正在尝试完全删除我安装的软件包go get来自 GitHub 这go clean i
  • 在golang中获取TTFB(第一个字节的时间)值

    我正在尝试获取 TTFB 值和 Connect 值 c exec Command curl w Connect time connect TTFB time starttransfer Total time time total o dev
  • 使用 OpenTelemetry 统一不同服务的范围

    我刚刚开始使用 OpenTelemetry 并为此创建了两个 微 服务 Standard and GeoMap 最终用户将请求发送到Standard服务 该服务又将请求发送到GeoMap在将结果返回给最终用户之前获取信息 我使用 gRPC
  • Go 的范围不能超过 (类型接口 {})

    我正处于尝试将我的注意力集中在 Go 上的婴儿阶段 目前 我正在模拟一个 API 请求 该请求返回包含对象数组的 JSON 格式的字符串 我试图找出迭代每个记录并访问每个字段的最合适的方法 最终 每个字段都将写入 Excel 电子表格 但现
  • 编写每个处理程序中间件

    我希望从处理程序中提取一些重复的逻辑 并将其放入一些每个处理程序的中间件中 特别是 CSRF 检查 检查现有会话值 即身份验证或预览页面 等 我读了关于此的几篇文章 http justinas org writing http middle
  • grpc 活跃客户端/通道数量

    我正在实现一个简单的 grpc 服务发现 我想做的一件事是跟踪当前有多少客户端使用该服务 并且该服务会将其报告给服务注册表 grpc server api 是否提供此类信息 我在这里遇到了一个有点类似的问题 https github com
  • Go 编程语言中的“方法需要指针接收器”

    我刚刚看到了 Go 编程语言的演示 并想尝试写几行 一切工作正常 直到我尝试在这种情况下使用界面 我该如何解决这个问题 package main import fmt type entity float32 func e entity in

随机推荐

  • windows10右下角网络图标不见,而且设置里面是灰色的

    打开任务管理器 把资源管理器右键重新启动
  • Variable used in lambda expression should be final or effectively final报错解决方案

    Variable used in lambda expression should be final or effectively final报错解决方案 问题背景 解决方案 Lyric 哪里都是你 问题背景 在使用forEach报错 Va
  • 比double精度更高的数据类型_数据类型

    两个重点 数据类型 变量 数据类型 Data Type 数据类型就是数据的类型 每种数据有自己的值和操作规则 C 有三大类语句类型 简单数据类型 结构化据类型 点 这一期只详细讲简单数据类型 另外两个类型的会分别单独做一期 简单数据类型 S
  • Faster Rcnn

    一 公用特征Feature Maps的获取 二 Region Proposal Network Feature Maps bs 1024 38 38 经过3 3卷积 然后分别经过两个1 1的卷积 通道数分别为18 36 18 9 2 代表每
  • JDBC-查询数据(封装工具类)

    使用封装工具类方式查询数据 1 封装注册 连接 关闭工具类 public class JdbcUtils 设置属性 驱动 private static String driver 连接 private static String url m
  • 算法课程设计--A*算法解决特定条件下的最短路径问题

    1 算法课设题目 LOL 峡谷地图最优路径规划 以下问题的计算 按照该地图的基本规则来进行在该地图中分布着各种形状不规则的障碍区域环境 整个地图模型 可以根据需求进行自行简化 问题一 在任意起点与终点之间 规划一条最短路径 问题二 当你拥有
  • 亚信科技AntDB数据库参与“国内首款”事务型性能测试工具开源发布会,树立金融技术风向标

    日前 在中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会共同举办的 国内首款金融数据库性能测试工具DataBench T开源发布会 上 亚信科技受邀进行了 亚信科技数据库测试之道 的主旨演讲 图1 中国信通院DataBench
  • 基于TensorFlow让机器生成赵雷曲风的歌词

    本文转载自公众号量化投资与机器学习 ZXL LHTZ JQXX 原文 致敬赵雷 基于TensorFlow让机器生成赵雷曲风的歌词 责编 王艺 投稿及采访请邮件wangyi csdn net 或 扫描文末二维码 我们基本上收集了赵雷所有唱过的
  • 狂神说Es

    在学习ElasticSearch之前 先简单了解一下Lucene Doug Cutting开发 是apache软件基金会4 jakarta项目组的一个子项目 是一个开放源代码的全文检索引擎工具包 不是一个完整的全文检索引擎 而是一个全文检索
  • MyBatis-Plus系列(一)--MyBatis-Plus集成Druid环境搭建

    一 简介 Mybatis Plus是一款 MyBatis 动态 sql 自动注入 crud 简化 增 删 改 查 操作中间件 启动加载 XML 配置时注入 mybatis 单表 动态 SQL 操作 为简化开发工作 提高生产率而生 Mybat
  • 大数据教育平台数仓实时计算实现 附安装包与脚本

    一 Spark Streaming Spark Streaming 是核心Spark API的扩展 可实现实时数据的可扩展 高吞吐量 容错处理 数据可以从许多来源 如Kafka Flume Kinesis 或TCP套接字 中获取 并可以使用
  • 模型训练评估——交叉验证法的介绍

    与留出法相似 将数据集D划分为k个子集同样存在多种划分方式 为减小因样本划分不同而引入的差别 k折交叉验证通常要随机使用不同的划分重复p次 最终的评估结果是这p次k折交叉验证结果的均值 例如常见的有 10次10折交叉验证 假定数据集D中包含
  • python opencv键盘监听

    目录 读取图片监听 opencv pyinput 监听小键盘 读取图片监听 for file in files a cv2 imread path file cv2 imshow a a k cv2 waitKey 10 0xFF if k
  • 292. Nim Game

    class Solution public bool canWinNim int n return n 4 0 my first solution if n 4 0 return false else return true
  • 新浪博客插入代码(块)及代码高亮…

    新浪博客不能插入代码是一件非常尴尬的事情 这里将介绍如何在新浪博客中加入代码块及其高亮的方法 所需工具 1 XAMPP数据创建软件 2 wordpress 与 codecolorer 3 CSDN博客 步骤 1 安装wordpress 与
  • pycharm IDE报错 ,记事本打开后出现卡顿,之后出现ide错误

    打开IDE之后进行编辑 点击setting出现页面无反应 右下角出现的提示 ide error occurred see detail submit display 点击进去之后 在点击这个display 类似重启IDE的命令 然后就可以继
  • 微服务架构超详细解析,写得太好了!

    本文将介绍微服务架构和相关的组件 介绍他们是什么以及为什么要使用微服务架构和这些组件 本文侧重于简明地表达微服务架构的全局图景 因此不会涉及具体如何使用组件等细节 为了防止不提供原网址的转载 特在这里加上原文链接 https www cnb
  • 什么是IPU?

    在图像识别的SOC中 有一个很重要的单元 IPU Image Processing Unit 图像处理单元 图像处理单元的目标是提供从图像输入 摄像头传感器 电视信号输入等 到显示设备 LCD显示屏 TV输出 外部图像处理单元等 端到端的数
  • 探索OLED拼接屏的特点及在莱山的场景化应用

    涞山oled拼接屏是一种高清晰度的显示屏 由多个oled屏幕拼接而成 它可以用于各种场合 如商业展示 广告宣传 会议演示等 涞山oled拼接屏具有以下特点 1 高清晰度 oled屏幕具有高对比度 高亮度 高色彩饱和度等特点 可以呈现出非常清
  • go-kit grpc调用及中间件封装

    存在问题 grpc 调用问题 通常我们向业务返回会定义如下的结构 code 20000 msg Success data 但是如果我们定义如下的proro grpc的返回值可以在客户端不能直接使用 还需要使用json进行解析 message