『InfoQ』Go 语言的微吐槽,这是一篇实践者对


『InfoQ』Go 语言的微吐槽,这是一篇实践者对
文章图片
作者|sbstp
译者|王强
策划|赵钰莹
本文作者最近开始在工作中将Go作为主力编程语言来使用 , 这是一种有趣的语言 , 带有丰富的标准库 , 但在标准库中交付一个生产就绪的HTTP服务器并非易事 。 因此 , 作者写下了这篇文章 , 提到了Go语言的一些问题 。
本文最初发布于sbstp博客 , 经原作者授权由InfoQ中文站翻译并分享 , 未经许可禁止一切形式的转载
在这篇文章中 , 我将讨论在使用Go语言的过程中遇到的一些问题和怪癖 。 我会有意略过那些经常被提到的问题 , 例如缺少泛型和err!=nil错误处理模式等 , 因为关于它们的讨论已经够多了 , 并且Go团队准备在Go2中解决它们 。
问题目录
零初始化
过度linting
返回错误
nil切片和JSON
Go模块和Gitlab
日期格式API
非类型化常量
总结
零初始化
Go允许变量和struct字段不使用一个值也能显式初始化 。 在这种情况下 , 它将为变量或字段赋予一个零值 , 我认为这可能成为错误和意外行为的潜在源头 。
我第一次遇到这方面的问题 , 是一个微服务开始用尽文件描述符 , 并因此出现虚假错误的时候 。 以下是导致问题出现的代码:
client:=&http.Client{Transport:&http.Transport{TLSClientConfig:&tls.Config{InsecureSkipVerify:true},}}
乍一看代码没什么问题 , 但实际上它包含一个导致TCP套接字泄漏的错误 。 这里发生的事情是 , 我们创建了一个新的http.Client , 其中所有传输超时都未指定 。 由于它们未被指定 , 因此Go会将它们初始化为零值 , 这就是问题所在 。
//IdleConnTimeoutisthemaximumamountoftimeanidle//(keep-alive)connectionwillremainidlebeforeclosing//itself.//Zeromeansnolimit.IdleConnTimeouttime.Duration//Go1.7
在上面的http.Transport文档中 , 可以看到零值表示超时是无限的 , 因此连接永远不会关闭 。
随着时间的流逝 , 套接字不断积累 , 最终导致文件描述符用尽 。 这一过程所需的时间取决于你的服务获得多少活动 , 以及文件描述符的ulimit设置 。
解决这个问题的方法很简单:初始化http.Transport时提供非零超时 。 StackOverflow网站上有这个问题的答案 , 演示了如何从http库复制默认值 。
但这仍然是一个容易掉进去的陷阱 , 据我所知目前没有lint可以帮助解决这类问题 。
这还会带来其他副作用 。 例如 , 未导出字段将始终被初始化为零值 , 因为字段无法从包外部进行初始化 。
下面是一个示例包:
packageutils
typeCollectionstruct{itemsmap[string]string}
func(c*Collection)Set(key,valstring){c.items[key]=val}
下面是这个包的用法示例:
packagemain
funcmain(){col:=utils.Collection{}col.Set("name","val")//panic:assignmenttonilmap}
解决这个问题的方法没那么优雅 。 这是防御性编程 , 在访问映射前 , 包作者必须检查它是否已经被初始化:
func(c*Collection)Set(key,valstring){ifc.items==nil{c.items=make(map[string]string)}c.items[key]=val}
如果struct具有多个字段 , 代码很快会变得臃肿 。 一种解决方案是为类型提供构造函数 , 例如utils.NewCollection() , 其会始终初始化字段 , 即便有了这个构造函数 , 也无法阻止用户使用utils.Collections{}初始化其结构 , 结果就是带来一堆问题 。
过度linting
我认为编译器对未使用的变量过于严格 。 我经常遇到的麻烦是 , 注释了一个函数调用后还得在调用上方修改多行代码 。
我有一个API客户端 , 可以在其上发送请求和接收响应 , 如下: