GOPATH
早期Go
语言使用GOPATH
来进行依赖管理。安装Go
语言之后,我们需要配置环境变量,GOROOT
,GOPATH
。GOROOT
代表Go
语言的安装目录,GOPATH
代表你的工作路径。
GOPATH
下的目录结构
- – bin 存放编译后生成的二进制可执行文件
- – pkg 存放编译后生成的
.a
文件 - – src 存放项目的源代码,可以是你自己写的代码,也可以是你 go get 下载的包
GOPATH
├── bin
├── pkg
└── src
├── projectA
├── projectB
└── projectC
将你的包或者别人的包全部放在 $GOPATH/src
目录下进行管理的方式,我们称之为GOPATH
模式。
使用 GOPATH 管理的缺点
版本管理问题
GOPATH
根本没有版本的概念,GOPATH
的src
下只能有一个版本存在,所以不能实现多版本控制。
如果你所应用的库需要升级,那就是全局升级,所以涉及这个库的服务在下一次编译中都会使用新的库,这是一件很危险的事情。版本管理非常重要,自己应该管理好自己的引用库。
协同开发问题
当其他的开发者get
到源码进行修改的时候,你无法保证他下载的包是不是你所期望的版本,这及有可能导致服务出错,且很难查找原因。
GOVENDOR
为了解决 GOPATH
方案下不同项目下无法使用多个版本库的问题,Go v1.5
开始支持 vendor
。我们之前是所有项目都放到GOPATH
的src
目录下进行管理,所有项目的依赖都在这里寻找。并且只能存在一个版本。但是每个项目可能引用的第三方库的版本不一样,我们需要满足多版本控制。
于是,现在可以在每个项目下建立vector
目录,每个项目所需的依赖都只会下载到自己vendor
目录下,项目之间的依赖包互不影响。
如果该项目要查找引用的包,那么会按照如下顺序进行搜索
- 先搜索当前包下的
vendor
目录 - 向上级目录搜搜,直到
src
下的vendor
目录 - 在
GOROOT
目录下搜索 - 在
GOPATH
目录下搜索
.
└── GOPATH
├── bin
├── pkg
└── src
├── projectA
│ ├── handler
│ ├── server.go
│ └── vendor
├── projectB
│ ├── handler
│ ├── server.go
│ └── vendor
└── projectC
├── handler
├── server.go
└── vendor
使用 GO VENDOR 管理的缺点
假设一个Project
依赖A
,B
两个package
,然后A
依赖第三方的package v1
,B
依赖第三方的package v2
,这就会导致冲突。
仍然需要在GOPATH
目录下。
GO MOD
之后Go
引入了go mod
,go mod
不在依赖$GOPATH
目录,因此我们可以在其他地方创建项目,而不一定要在$GOPATH/src
目录下。
go mod
模式可以手动开启关闭,通过GO111MODULE
实现。
GO111MODULE=off
禁用模块支持,编译时会从GOPATH
和vendor
文件夹中查找包。GO111MODULE=on
启用模块支持,编译时会忽略GOPATH
和vendor
文件夹,只根据go.mod
下载依赖。GO111MODULE=auto
,当项目在$GOPATH/src
外且项目根目录有go.mod
文件时,自动开启模块支持。
使用go env
查看环境变量,可以看到GO111MODULE=on
,说明我启动了go mod
模式管理依赖
go env
PS D:\Go\workspaces\src> go env
set GO111MODULE=on
set GOPATH=D:\syz\study\computer_science\Go\workspaces
set GOPROXY=https://goproxy.cn,direct
set GOROOT=D:\Program Files (x86)\go
如何使用go mod
创建一个项目,在此文件夹下输入go mod init
初始化(记得开启go mod
模式),之后变会使用go mod
模式管理。之后,会在你的目录下创建go.mod
文件,编译后还会出现go.sum
文件。
我们在简单的hello world
程序下引入gin
框架,可以看到go.sum
文件出现了变动,go mod
会将所有的依赖包安装在$GOPATH/pkg
目录下。
go.mod文件
- 第一行:模块的引用路径
- 第二行:项目使用的 go 版本
- 第三行:项目所需的直接依赖包及其版本
module test
go 1.13
require github.com/gin-gonic/gin v1.7.7
go.sum文件
每一行都是由 模块路径
,模块版本
,哈希检验值
组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash 是以h1:
开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
...
有的包可能有两行
<module> <version> <hash>
<module> <version>/go.mod <hash>
那些有两行的包,区别就在于hash
值不一行,一个是 h1:hash
,一个是 go.mod h1:hash
而 h1:hash
和 go.mod h1:hash
两者,要不就是同时存在,要不就是只存在 go.mod h1:hash
。那什么情况下会不存在 h1:hash
呢,就是当Go
认为肯定用不到某个模块版本的时候就会省略它的h1 hash
,就会出现不存在 h1 hash
,只存在 go.mod h1:hash
的情况。
注意
如果你开启了go mod
模式,但是又没有go mod init
,那么是无法进行依赖管理的,也不会按照GO PATH
的方法进行依赖查找。