Go Modules踩坑总结

栏目: Go · 发布时间: 5年前

内容简介:在 Java 的项目中,有 Maven 和 Gradle 这些很好用的依赖版本管理工具,简直不要太方便了,但是在 Golang 的项目中,之前的 Golang 官方并没有提供版本管理工具,我们以前用 go get 获取依赖其实是有潜在危险的,因为我们不确定最新版依赖是否会破坏掉我们项目对依赖包的使用方式,即当前项目可能会出现不兼容最新依赖包的问题。之后官方出了一个 vendor 机制,将项目依赖的包都放在该目录中,但这也并没有很好地管理依赖的版本。之后官方出了一个准官方版本管理工具 go dep,这也算是

Java 的项目中,有 Maven 和 Gradle 这些很好用的依赖版本管理工具,简直不要太方便了,但是在 Golang 的项目中,之前的 Golang 官方并没有提供版本管理工具,我们以前用 go get 获取依赖其实是有潜在危险的,因为我们不确定最新版依赖是否会破坏掉我们项目对依赖包的使用方式,即当前项目可能会出现不兼容最新依赖包的问题。之后官方出了一个 vendor 机制,将项目依赖的包都放在该目录中,但这也并没有很好地管理依赖的版本。之后官方出了一个准官方版本管理工具 go dep,这也算是 go modules 的前身了吧。随着 Go 1.11 的发布,Golang 给我们带来了 module 全新特性,这是 Golang 新的一套依赖管理系统。现在 Go1.12 已经发布了,go modules 进一步稳定,但官方还是没有将其设为默认机制,所以踩坑之路是必须的,本篇文章除了详细说明 go modules 的特性以及使用之外,还总结了我在这个过程中遇到的一些“坑”。

创建 module

创建项目

在默认情况下,$GOPATH 默认情况下是不支持 go mudules 的,我们需要在项目目录下手动执行以下命令:

$ export GO111MODULE=on

这也表明了 go 要利用 modules 机制消灭 $GOPATH 的决心啊!

为了配合 go modules 机制,我们 $GOPATH 以外的目录创建一个 testmod 的包:


 

$ mkdir testmod

$ cd testmod

$ echo 'package testmod

import "fmt"

func SayHello(name string) string {

return fmt.Sprintf("Hello, %s", name)

}' >> testmod.go

初始化 module:


 

$ go mod init github.com/objcoding/testmod

go: creating new go.mod: module github.com/objcoding/testmod

以上命令会在项目中创建一个 go.mod 文件,初始化内容如下:

module github.com/objcoding/testmod

这时,我们的项目已经成为了一个 module 了。

推送到 github 仓库


 

$ git init

$ git add *

$ git commit -am "First commit"

$ git push -u origin master

在这里我也着重说下关于项目依赖包引用地址的问题,这个问题虽小,但也确实很困扰人,所以必须得说一下:

go mudules 出现之前,在一个项目中有很多个包,在项目内,有些包需要依赖项目内其它包,假设项目有个包,相对于 gopath 的地址是 objcoding/mypackage,在项目内其它包引用这个包时,就可以通过以下引用:

import myproject/mypackage

但你有没有想过,当别的项目需要引用你的项目中的某些包,那么就需要远程下载依赖包了,这时就需要项目的仓库地址引用,比如下面这样:

import github.com/objcoding/myproject/mypackage

go modules 发布之后,就完全统一了包引用的地址,如上面我们说的创建 go.mod 文件后,初始化内容的第一行就是我们说的项目依赖路径,通常来说该地址就是项目的仓库地址,所有需要引用项目包的地址都填写这个地址,无论是内部之间引用还是外部引用,举个例子,goim 的内部包引用:

go.mod module:

Go Modules踩坑总结

内部包引用:

Go Modules踩坑总结

也即是说,在项目 启用了 go modules 之后,引用包必须跟 go mod 文件第一行包名一样,

依赖的包都会保存在 ${GOPATH}/pkg/mod 文件夹中了,我们也可以在项目底部那里查看依赖包:

Go Modules踩坑总结

但也有可能会出现依赖包地址正确但会报红的情况,这时极有可能是你在 Goland 编辑器中没有将项目设置为 go modules 项目,具体设置如下:

Go Modules踩坑总结

勾选了该选项之后,就会在 External Libraries 中出现 Go Modules 目录。

版本规则

go modules 是一个版本化依赖管理系统,版本需要遵循一些规则,比如版本号需要遵循以下格式:


 

vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef

vX.0.0-yyyymmddhhmmss-abcdefabcdef

vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef

vX.Y.Z

vX.Y.Z 是我们仓库打的标签版本,也就是 go modules 是根据仓库标签来确定版本号的,因此我们发布版本时,需要给我们的仓库打上一个标签版本号。

也就是版本号 + 时间戳 +hash,我们自己指定版本时只需要制定版本号即可,没有版本 tag 的则需要找到对应 commit 的时间和 hash 值。

还有一个重要的规则是,版本 0 和 1,最好需要有不同的依赖路径,如:v1.0.0 和 v2.0.0 是有不同的依赖路径,下面会详细介绍一下这个版本规则。

发布版本

了解了 go modules 的版本规则后,现在我们发布一下该项目的版本:


 

$ git tag v1.0.0

$ git push --tags

这时我们最好还需要创建一条 v1 分支,以便我们在其它分支写代码不会影响到 v1.0.0 版本:


 

$ git checkout -b v1

$ git push -u origin v1

使用 module

现在我们把刚刚做好的 module,拿过来用,新建一个 gomodules 项目:


 

package main


import (

"fmt"

"github.com/objcoding/testmod"

)


func main() {

fmt.Println(testmod.SayHello("张乘辉"))

}

以前我们可以直接通过 go get 命令获取依赖包,但是对于一个 module 项目来说,就远远比这个有趣多了,现将项目初始化成 module 项目:

$ go mod init

这时 go build 等命令就会下载依赖包,并把依赖信息添加到 go.mod 文件中,同时把依赖版本哈希信息存到 go.sum 文件中:


 

$ go build


go: finding github.com/objcoding/testmod v1.0.0

go: downloading github.com/objcoding/testmod v1.0.0

这时,go.mod 文件内容如下:


 

module gomodules

require github.com/objcoding/testmod v1.0.0

go.sum 文件内容如下:


 

github.com/objcoding/testmod v1.0.0 h1:fGa15gBXoqkG0BVkQGP9i5Pg2nt8nayFpHFf+GLiX6A=

github.com/objcoding/testmod v1.0.0/go.mod h1:LGpYEmOLZhLQC3JW88STU2Ja3rsfoGZmsidsHJhDNGU=

这里还需要注意的是,有时候我们引用 golang.org/x/ 的一些包,但发现在伟大的天朝这个地址是被qian了,但是我们 程序员 也充分发挥了勤奋好学的优良传统,在 go modules 中设置了 goproxy 机制,如果 go modules 设置了代理,会优先从代理中下载依赖包,在 /etc/profile 中加入以下内容:

export GOPROXY="https://goproxy.io"

goproxy.io 谷歌官方的代理地址,当然还有很多国内优秀的第三方代理。

你也可以在 Goland编辑器中设置:

Go Modules踩坑总结

升级版本

现在我们来升级一下 testmod 项目:


 

$ cd gomodules

$ echo 'package testmod

import "fmt"

func SayHello(name string) string {

return fmt.Sprintf("你好, %s", name)

}' >> testmod.go

我把「Hello」改成「你好」,我们把这个修改在 v1 分支中进行:


 

$ git commit -m "update testmod" testmod.go

$ git tag v1.0.1

$ git push --tags origin v1

现在我们的 项目已经升级到 v1.0.1 版本了,我们可以有多种方式获取这个版本依赖,go1.11 中,go get 拥有了很多新特性,我们可以直接通过以下命令获取 v1.01 版本依赖:

$ go get github.com/objcoding/testmod@v1.0.1

也可以通过 go mod 命令:


 

$ go mod edit -require="github.com/objcoding/testmod@v1.0.1"


$ go mod tidy

go mod edit -require 可以主动修改 go.md 文件中依赖的版本号,然后通过 go mod tidy 对版本进行更新,这是一条神一样的命令,它会自动清理掉不需要的依赖项,同时可以将依赖项更新到当前版本。

主要版本升级

上面版本规则说了,版本 0 和 1,即大版本更新,最好需要有不同的依赖路径,如:v1.0.0 和 v2.0.0 是有不同的依赖路径,那么用 go modules 怎么实现呢,我们可以通过修改 go.mod 文件第一行添加新路径:


 

$ cd testmod

$ echo 'module github.com/objcoding/testmod/v2' >> go.mod

然后我们修改 testmod 函数 Hi():


 

$ cd testmod


$ echo 'package testmod

import (

"fmt"

)

func SayHello(name, str string) string {

return fmt.Sprintf("你好, %s, %s", name, str)

}' >> testmod.go

这时,SayHello() 方法将不兼容 v1 版本,我们需要新建一个 v2.0.0 版本,还是老样子,我们最好在 v2.0.0 版本新建一条 v2 分分支,将 v2.0.0 版本的代码写到这条分支中(这只是一个规范,实际上你将代码也写到任何分支中都行,Go并没有这个规范):


 

$ git add *

$ git checkout -b v2

$ git commit testmod.go -m "v2.0.0"

$ git tag v2.0.0

$ git push --tags origin v2

这时候 testmod 的版本已经更新到 v2.0.0 版本了,该版本并不兼容以前的版本,但是目前项目依然只能获取到 v1.0.1 的依赖版本,因为我们 testmod 的 module 路径已经加上 v2 了,因此并不会出现冲突的问题,那么如果我们需要使用 v2.0.0 版本呢?我们只需要在项目中更改一下 import 路径:


 

package main


import (

"fmt"

"github.com/objcoding/testmod/v2"

)

func main() {

fmt.Println(testmod.SayHello("张乘辉", "最近过得怎样"))

}

执行:

go mod tidy

这时我们把 testmod 依赖版本号更新到了 v2.0.0 版本了,虽然是此时的 import 路径是以 “v2” 结尾,但是 Go 很人性化,我们依然可以使用 testmod 来使用。

Go 团队表示,在 Go 1.12 之前,这个特性都将会处于实验性阶段,Go 团队会努力保持兼容性。一旦模块稳定之后,对 GOPATH 的支持将会被移除掉。

近期热文

我对支付平台架构设计的一些思考

聊聊Tomcat的架构设计

从源码的角度解析线程池运行原理

RocketMQ消息发送的高可用设计

深度解析RocketMQ Topic的创建机制

RocketMQ源码分析之路由中心

RocketMQ的消费模式

mybatis-plus源码分析之 sql 注入器

钟同学,this is for you!

Mybatis-spring源码分析之注册Mapper Bean

基于Jenkins Pipeline自动化部署

Dubbo服务暴露之注册地址和端口

Dubbo全链路追踪日志的实现

Go Modules踩坑总结

长按可以订阅


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

软利器

软利器

保罗·莱文森 / 何道宽 / 复旦大学出版社 / 2011-5 / 35.00元

《软利器:信息革命的自然历史与未来》内容简介:何谓“软利器”?一种轻盈、透明、无质无形、难以把握的力量,由信息和物理载体构成,这就是媒介。了解媒介的属性和演化规律的人,常占尽优势:反之则身处险境。是不是有些危言耸听? 如果你看过保罗•莱文森的这本《软利器:信息革命的自然历史与未来》,或许就会深信不疑。在书中,莱文森如同一位经验丰富的航海家,带领你穿越媒介时空——你将邂逅古埃及的法老、古希腊的......一起来看看 《软利器》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具