Browsed by
分类:golang

Go module使用学习

Go module使用学习

Go 1.11 前天已经正式发布了,这个版本包含了两个最重要的feature就是moduleweb assembly。虽然也有一些简单的教程介绍了go module的特性,但是基本上都是hello world的例子,在实践的过程中, 很多人都在“拼命的挣扎”,包括我自己, 从一些qq群、github的issue, twitter上都可以看到大家茫然或者抱怨的语句。

虽然有三个帮助文件go help modgo help modulesgo help module-get可以了解一些go module的用法,但是感觉Go开发组对module这一特性还是没有很好的做一个全面的介绍,很多情况还得靠大家看源代码或者去猜,比如module下载的文件夹、版本格式的完整声明,module的最佳实践等,并且当前Go 1.11的实现中还有一些bug,给大家在使用的过程中带来了很大的困难。

我也在摸索中前行, 记录了摸索过程中的一些总结,希望能给还在挣扎中的Gopher一些帮助。

Introduction to Go Modules 是一篇很好的go module 入门介绍, 如果你仔细阅读了它,应该就不需要看本文了。

GO111MODUL

要使用go module,首先要设置GO111MODULE=on,这没什么可说的,如果没设置,执行命令的时候会有提示,这个大家应该都了解了。

既有项目

假设你已经有了一个go 项目, 比如在$GOPATH/github.com/smallnest/rpcx下, 你可以使用go mod init github.com/smallnest/rpcx在这个文件夹下创建一个空的go.mod (只有第一行 module github.com/smallnest/rpcx)。

然后你可以通过go get ./...让它查找依赖,并记录在go.mod文件中(你还可以指定 -tags,这样可以把tags的依赖都查找到)。

通过go mod tidy也可以用来为go.mod增加丢失的依赖,删除不需要的依赖,但是我不确定它怎么处理tags

执行上面的命令会把go.modlatest版本换成实际的最新的版本,并且会生成一个go.sum记录每个依赖库的版本和哈希值。

新的项目

你可以在GOPATH之外创建新的项目。

go mod init packagename可以创建一个空的go.mod,然后你可以在其中增加require github.com/smallnest/rpcx latest依赖,或者像上面一样让go自动发现和维护。

go mod download可以下载所需要的依赖,但是依赖并不是下载到$GOPATH中,而是$GOPATH/pkg/mod中,多个项目可以共享缓存的module。

go mod 命令

download    download modules to local cacheedit        edit go.mod from tools or scriptsgraph       print module requirement graphinit        initialize new module in current directorytidy        add missing and remove unused modulesvendor      make vendored copy of dependenciesverify      verify dependencies have expected contentwhy         explain why packages or modules are needed

有些命令还有bug, 比如go mod download -dir:

go mod download -dir /tmpflag provided but not defined: -dirusage: go mod download [-dir] [-json] [modules]Run 'go help mod download' for details.

帮助里明明说可以设置dir,但是实际却不支持dir参数。

看这些命令的帮助已经比较容易了解命令的功能。

翻墙

在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。

replace (golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dacgolang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638dgolang.org/x/text v0.3.0 => github.com/golang/text v0.3.05)

依赖库中的replace对你的主go.mod不起作用,比如github.com/smallnest/rpcxgo.mod已经增加了replace,但是你的go.mod虽然requirerpcx的库,但是没有设置replace的话, go get还是会访问golang.org/x

所以如果想编译那个项目,就在哪个项目中增加replace

版本格式

下面的版本都是合法的

gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7gopkg.in/vmihailenco/msgpack.v2 v2.9.1gopkg.in/yaml.v2 <=v2.2.1github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3elatest

go get升级

  • 运行go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  • 运行go get -u=patch 将会升级到最新的修订版本
  • 运行go get package@version 将会升级到指定的版本号version

go mod vendo

go mod vendor 会复制modules下载到vendor中, 貌似只会下载你代码中引用的库,而不是go.mod中定义全部的module。

go module,vendor和travis C

https://arslan.io/2018/08/26/using-go-modules-with-vendor-support-on-travis-ci/

阅读全文
golang api中避免内存泄露

golang api中避免内存泄露

你有必要在把golang api投入生产环境前阅读下这篇文章,基于我们的真实经历,因为没有使用正确的方法,我们在每个release都很遇到困难和挣扎。

就在几周之前,我们刚刚修复了一个奇怪的未发现的bug,我们试了很多方法去debug和修复它。这个bug并不是业务逻辑上的问题,因为它已经在生产环境跑了几周了,那是因为我们的自动释放机制,隐藏了这个问题,所以它看上去是正常。

直到最近,我们发现这个问题是因为代码并没有处理好。

架构

首先说下,我们在架构中使用了微服务模式。我们有一个gateway API - 我们叫“main API”,它服务于我们的用户(手机客户端和web页面)。由于它的角色很像API Gateway,所以它的任务就只是处理用户的请求,然后再请求道对应的服务上,然后将reponse返回给用户。这个“main API”完全是使用Golang开发的。为什么选择golang在这里就不详细介绍了。

我们系统的架构图大概如下

问题

我们被main API困扰了好久,他经常down掉,并且返回给...

阅读全文
golang使用vendor目录来管理依赖包

golang使用vendor目录来管理依赖包

最新的go推荐使用go mohttp://www.gaoxuan1989.com/2019/07/19/using-go-mod/

Vendor目录介绍

随着Go 1.5 release版本的发布,vendor目录被添加到除了GOPATHGOROOT之外的依赖目录查找的解决方案。在Go 1.6之前,你需要手动的设置环境变量GO15VENDOREXPERIMENT=1才可以使Go找到Vendor目录,然而在Go 1.6之后,这个功能已经不需要配置环境变量就可以实现了。
Note,即使使用vendor,也必须在GOPATH中,在go的工具链中,你逃不掉GOPATH
那么查找依赖包路径的解决方案如下
  • 当前包下的vendor目录。
  • 向上级目录查找,直到找到src下的vendor目录。
  • GOPATH下面查找依赖包。
  • GOROOT目录下查找

一些建议

在使用vendor中,给出如下建议
  1. 一个库工程(不包含main的package)不应该在自己的版本控制中存储外部的包在vendor\目录中,除非他们有特殊原因并且知道为什么要这么做。
  2. 在一个应用中,(包含main的package),建议只有一个vendor目录在代码库一级目录。
上面建议的原因如下
  • 在目录结构中的每个包的实例,即使是同一个包的同一个版本,都会打到最终的二进制文件中,如果每个人都单独的存储自己的依赖包,会迅速导致生成文件的二进制爆发binary bloa
  • 在一个目录的某个pacage类型,并不兼容在同一个package但是在不同目录的类型,即便是同一个版本的package,那意味着loggers,数据库连接,和其他共享的实例都没法工作。

举个例子

工程目录如下
- $GOPATH/src/github.com/mattfarina/golang-broken-vendor- foo.go- vendor/   - a/   - b/       - vendor/a/
在这个例子中,两个apackage都是完全一样的,b package在代码库中保存了a package,在顶级应用代码中也引用了a包。文件foo.go做了很简单的事情:
funmai() vai.i"foo".Di
那么问题来了,当我们build的时候,发现出问题了,返回了下面的错误
$ GO15VENDOREXPERIMENT=1 go build./foo.go:12: cannot use it (type "github.com/mattfarina/golang-broken-vendor/vendor/a".A) as type "github.com/mattfarina/golang-broken-vendor/vendor/b/vendor/a".A in argument to b.Do
你可以clone这个测试...阅读全文