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

golang编码最佳实践(持续更新中)

最近在学习go语言,以此记录日常编码中的最佳实践,欢迎大家一起讨论

注释模板

使用Goanno插件:https://github.com/loveinsky100/goanno

设置模板

以goland为例

  1. 选择“工具-Goanno设置”

2. 编辑模板

// ${function_name} ${todo}
//  @receiver ${receiver}
//  @param ${params}
//  @return ${return_types}

案例

// GetNode 获取指定结构,从缓存加载
//
//	@receiver c
//	@param ctx
//	@param countryCode	国家码
//	@return *Node
func (c *Cache) GetNode(ctx context.Context, countryCode string) *Node, error {...return cacheNode, nil
}

 日志打印

建议使用zap的零内存分配api,性能比go标准库的api好:GitHub - uber-go/zap: Blazing fast, structured, leveled logging in Go.

格式处理

  • %v: 根据值的类型自动选择合适的格式输出。
  • %+v: 类似 %v,但会输出更多的信息,如字段名称。
  • %#v: 输出值的完整 Go 语法表示,包括类型信息。
  • 不要直接打印byte[]类型等无实际意义的日志,应该转成string打印。

错误处理

go最受争议的部分之一就是错误处理,这里不去讨论其好坏,仍然推荐大家使用官方的处理方式:返回error接口,不建议使用panic + recover,容易导致程序崩溃

func f() error {if ... {return errors.New("xxx")}return nil
}

解决error无堆栈

部分goer建议使用panic + recover就是因为其有堆栈而error没有,但我们可以实现新的error接口时增加堆栈记录

type BizError struct {code    stringmessage stringcause   errorstacktrace struct {onCreate *stacktraceonPanic  *stacktrace}
}// NewBizError create a new BizError
//   - cause can be nil is no underlying error
//   - omitStacks indicates how many frames should be dropped, if <=0 no stacks will be filled
func NewBizError(code api.ResultCode, message string, cause error, omitStacks int, throwInPlace bool) *BizError {err := &BizError{code:    code.Code(),message: message,cause:   cause,}if omitStacks >= 0 {err.stacktrace.onCreate = dumpStacktrace(omitStacks + 1)if throwInPlace {err.stacktrace.onPanic = err.stacktrace.onCreate}}return err
}func (this *BizError) Error() string {if len(this.message) > 0 {return this.code + ": " + this.message} else {return this.code + ": [NO_MESSAGE]"}
}type stacktrace struct {header []byte // goroutine header (e.g., "goroutine 3 [running]:")frames []byte // the stacktrace details
}func dumpStacktrace(skip int) *stacktrace {skip += 2 // skip debug.Stack and this frameskip *= 2 // 2-line each framestack := debug.Stack()var header []bytefor i, b := range stack { // assumes no unicode in stack, iterate on bytesif b == '\n' {if header == nil {// consume first line as goroutine headerheader = stack[:i]} else {skip--if skip == 0 {stack = stack[i:]break}}}}if skip > 0 {panic("skip overflow")}return &stacktrace{header, stack}
}

这样使用时即可记录堆栈

类型转换

因为 Golang 语言是强类型,所以经常会使用到类型转换,所以在这里推荐类型转换三方库:GitHub - spf13/cast: safe and easy casting from one type to another in Go

cast.ToString("mayonegg")         // "mayonegg"
cast.ToString(8)                  // "8"
cast.ToString(8.31)               // "8.31"
cast.ToString([]byte("one time")) // "one time"
cast.ToString(nil)                // ""var foo interface{} = "one more time"
cast.ToString(foo)                // "one more time"cast.ToInt(8)                  // 8
cast.ToInt(8.31)               // 8
cast.ToInt("8")                // 8
cast.ToInt(true)               // 1
cast.ToInt(false)              // 0var eight interface{} = 8
cast.ToInt(eight)              // 8
cast.ToInt(nil)                // 0

json工具

golang原生对json已经做了很好的支持,简单易用,但其性能一直为人垢病,因此建议使用字节开源的工具sonic。除了能平替原生的json使用姿势外,在性能上面也是从底层方面做了很多文章进行优化,性能方面遥遥领先:sonic:基于 JIT 技术的开源全场景高性能 JSON 库_原生云_火山引擎开发者社区_InfoQ写作社区

web/rpc服务

单独为某个下游接口设置超时时间

要知道go的超时时间不像java那样每个调用都固定超时时间,而是以总体时间来计算,但有时部分下游就是需要超出原超时时间进行(当然必须是异步调用,否则就自相矛盾了),具体代码如下:

// 注意:必须先cancel再设置,否则只能设置比原来时间更短的时间
func TestTimeout(t *testing.T) {ctx := context.Background()ctx, _ = context.WithTimeout(ctx, time.Second*3)ctx, _ = context.WithTimeout(ctx, time.Second*6)deadline, _ := ctx.Deadline()fmt.Println(deadline.Sub(time.Now())) // 2.99s, 直接覆盖设置不生效ctx, _ = context.WithTimeout(ctx, time.Second*1)deadline, _ = ctx.Deadline()fmt.Println(deadline.Sub(time.Now())) // 0.99s 设置更短时间, 生效ctx = context.WithoutCancel(ctx)                 // 取消ctx, _ = context.WithTimeout(ctx, time.Second*6) // 重新设置deadline, _ = ctx.Deadline()fmt.Println(deadline.Sub(time.Now())) // 5.99s, 取消后再设置, 才生效
}

proto参数校验

建议使用PGV:GitHub - bufbuild/protoc-gen-validate: Protocol Buffer Validation - Being replaced by github.com/bufbuild/protovalidate

这样会再生成一个proto的validate文件

建议consumer在远程调用前先调用validate方法,当参数不合法时提前感知

func remote(ctx context.Context) {request := ...if err := request.ValidateAll(); err != nil {return nil, errors.New(err.Error())}remoteClient.GetXXX(ctx, request)
}

provider在实现时也必须调用validate,防止参数不合法

func (s *server) Remote(ctx context.Context, request XXX) Response, error {if err := request.ValidateAll(); err != nil {return nil, errors.New(err.Error())}// 处理逻辑
}

但每个方法都要加validate重复代码,有没有办法统一处理参数校验呢,当然是有的

type validator interface {ValidateAll() error
}// ValidateAllInterceptor
//
//	@Description: grpc服务注册的参数校验拦截器
//	@return grpc.UnaryServerInterceptor
func ValidateAllInterceptor() grpc.UnaryServerInterceptor {return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (reply interface{}, err error) {if v, ok := req.(validator); ok {if err := v.ValidateAll(); err != nil {return nil, err}}return handler(ctx, req)}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • fastjson-1.2.24利用
  • ardupilot开发 --- Rpanion-server 篇
  • 通过 C# 写入数据到Excel表格
  • 【收集表单数据】
  • 通过颜色反卷积进行组织化学染色的定量分析
  • git submodule 使用
  • K8S可视化管理平台KubeSphere
  • 【Linux 网络】应用层
  • es之must、filter、must_not、should
  • 公交信息在线查询小程序的设计
  • 【vue3】template标签的一些理解(提了一嘴component标签)
  • 要抓住国际白银现货行情 以下这几点需要注意
  • Java毕业设计 基于SSM和Vue的美容院管理系统小程序
  • 科普文:Lombok使用及工作原理详解
  • 【Python数据结构与算法】递归----上台阶
  • [iOS]Core Data浅析一 -- 启用Core Data
  • [Vue CLI 3] 配置解析之 css.extract
  • 【翻译】babel对TC39装饰器草案的实现
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • cookie和session
  • css属性的继承、初识值、计算值、当前值、应用值
  • Mysql5.6主从复制
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • NSTimer学习笔记
  • opencv python Meanshift 和 Camshift
  • rabbitmq延迟消息示例
  • RxJS: 简单入门
  • XML已死 ?
  • 对超线程几个不同角度的解释
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 将回调地狱按在地上摩擦的Promise
  • 利用jquery编写加法运算验证码
  • 软件开发学习的5大技巧,你知道吗?
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  •  一套莫尔斯电报听写、翻译系统
  • 用jQuery怎么做到前后端分离
  • 阿里云ACE认证学习知识点梳理
  • 回归生活:清理微信公众号
  • 数据库巡检项
  • ​业务双活的数据切换思路设计(下)
  • "无招胜有招"nbsp;史上最全的互…
  • ### RabbitMQ五种工作模式:
  • #70结构体案例1(导师,学生,成绩)
  • #define与typedef区别
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (13):Silverlight 2 数据与通信之WebRequest
  • (2)nginx 安装、启停
  • (20)docke容器
  • (26)4.7 字符函数和字符串函数
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (day6) 319. 灯泡开关
  • (void) (_x == _y)的作用
  • (动态规划)5. 最长回文子串 java解决