可以关注我的公众号:【前端驿站Lite】,一个不止分享前端的地方 ᕦ( •̀∀•́)ᕤ
阅读收获
阅读完本篇文章,你将会有以下收获:
- GOPATH如何使用,有哪些缺点。
- Go Vendor如何使用,有哪些缺点。
- Go Modules 详细使用过程,解决了哪些问题。
- go install、go get、go mod等常用命令详解。
- GOPATH、GOROOT、GOBIN、GOPROXY、GO111MODULE、GOPRIVATE等环境变量详解。
使用go
下载安装golang以后,配置系统PATH
环境变量,设置为go安装目录下的bin目录
,这样我们就可以在终端使用go命令了,如:go version
、go env
、go run
等。
GO发展历史
在GO1.11之前,我们都是使用GOPATH
的方式来管理我们的项目的,但经过开发者们的实操检验,发现GOPATH
的方式,存在很多问题,关于GOPATH
方式带来的问题,我们下面会详细介绍。
因为GOPATH
的方式存在很多问题,所以GO1.11之后,官方推出了Go modules
的方式来管理我们的项目,发布于 Go1.11,成长于 Go1.12,丰富于 Go1.13,正式于 Go1.14 推荐在生产上使用。
现在,大多是情况下,我们都是使用Go modules
方式来管理我们的项目了。
为了更好的学习Go modules
,我们有必要先了解下如何使用GOPATH
方式管理项目,GOPATH
有哪些缺点需要改进。
GOPATH 是什么呢?
使用GOPATH
方式管理项目之前,我们需要先配置一个go的环境变量,叫做GOPATH
,可以执行go env
来查看该环境变量的值。
设置go env方式有两种:
- 第一种:使用
go env -w GOPATH=xxx
方式来设置。 - 第二种:直接在系统环境变量中设置环境变量
GOPATH
注意: 系统环境变量优先级最高,如果设置了系统级环境变量GOPATH
,无法使用go env -w GOPATH=xxx
方式去修改。
假设,我们目前的GOPATH为/Users/mac/go
,那么我们需要在/Users/mac/go
目录下创建三个子目录,分别是src
、pkg
、bin
:
/Users/mac/go
├── bin
├── pkg
└── src
这三个目录是干嘛的呢? 我们一一来讲解。 首先说一下src
目录。
GOPATH/src
GOPATH
方式强制规定,我们所有的go项目都必须在GOPATH/src
目录下,否则就无法使用各种go命令,来完成我们的开发工作。
假设,我们创建了一个项目my-project
。
当我们项目中需要引入三方包时,要使用go get
命令下载第三方依赖,如go get github.com/gin-gonic/gin
,go get
会将下载的三方包,放在GOPATH/src
目录下。
此时呢,我们的GOPATH/src
目录结构如下:
/Users/mac/go
├── bin
├── pkg
└── src
├── my-project
└── github.com
└── gin-gonic
└── gin
这时候就会发现GOPATH/src
目录下即有我们自己的项目,也有我们下载的三方包,这样会导致我们的项目管理起来比较混乱,不方便管理。
当我们导入一个包时,会先向GOROOT/src
下查找,如果找不到,再去GOPATH/src
下查找,如果还是找不到,就会报错。
go install
关于GOPATH/bin
与GOPATH/pkg
两个目录,我们会通过go install
去讲解。
go install
命令会将我们的go源代码编译生成二进制可执行文件,并将该可执行文件放到GOPATH/bin
目录下。
没错GOPATH/bin
目录,就是用来给go install
存放可执行文件的。
1. go install命令有两种使用方式:
- 加包名:
go install [package]
,该package必须在GOPATH/src
目录下,否则会报错。 - 不加包名:
go install
,该命令会将当前目录当作要编译的包,该目录也必须在GOPATH/src
目录下,否在会报错。
2. go install什么时候会生成可执行文件?
要编译的包下,必须有main
包,且main
包下必须有main
方法,才会生成可执行文件,文件名默认为包名。
3. go install 会生成 .a 文件
.a
文件是编译过程中产生的中间文件,当下次执行go install
时,会检查该包下的go源码有没有变动,如果没有变动,直接使用上次生成的.a
文件进行编译,加快编译速度。
GOPATH/pkg
目录的作用,就是用来存放.a
文件的。
4. 什么时候会用到 go install
有些场景下,我们需要把我们的go项目或三方包生成可执行文件去使用,此时,就要用到go install
命令了。
5. go install
与 go get
有什么区别
go get
会下载并安装三方包,也就说go get
包含了go install
的功能。go install
只能用来安装本地已经存在的依赖,不能用来下载依赖。
6. go install 注意事项
go install
的使用,有一个前提,我们的项目必须在GOPATH/src
目录下,否则会报错。
GOPATH 缺点
- 自己编写的包,和网络上的第三方包,都放置在
GOPATH/src
下,容易造成混乱,不方便管理。 - 项目中用到的依赖包,都需要手动
go get
下载,大项目来说非常麻烦 - 如果引入的三方包中又引入了其他三方包,就不好处理了,需要找到以后,使用
go get
去下载。 go get
没有版本的概念,团队合作中,很容易出现使用了不同版本的包,造成不必要的错误。- 协作开发时,需要统一各个开发成员本地
$GOPATH/src
下的依赖包。 - 引用的包引用了已经转移的包,而作者没改的话,需要自己修改引用。
GOPATH
方式管理项目存在太多不方便了,所以Go1.11之后,官方推出了Go modules
的方式来管理我们的项目。
Go Vendor
为了解决GOPATH
方式 三方依赖与项目都放在GOPATH/src
下的问题,Go1.5之后,官方推出了Go Vendor
的方式来管理项目。
go vendor
就是将依赖的包,特指外部包,复制到当前项目下的vendor
目录下,这样go build
的时候,go会优先从vendor
目录寻找依赖包。
Go Vender的出现解决了GOPATH不能多版本控制的问题。
但Go Vender
放弃了依赖重用,使冗余度上升。同一个依赖包如果不同工程想复用,都必须各自复制一份在自己的vendor
目录下。
开启方式
- golang 1.5默认是关闭的,需要设置
go env -w GO15VENDOREXPERIMENT=1
来手动开启。 - golang 1.6默认开启
- goalng 1.7 已去掉该环境变量,默认开启
vendor
特性。
govendor
那有了Vendor
特性,我们如何去用呢,总不能手动去搞吧? 于是govendor
就出现了。
govendor
是一个基于 vendor
机制实现的 Go 包依赖管理命令行工具。
# 1.下载安装
go get -u github.com/kardianos/govendor
# 2.初始化项目
govendor init
项目根目录下即会自动生成 vendor
目录和 vendor.json
文件。此时 vendor.json
文件内容为:
{
"comment": "",
"ignore": "test",
"package": [],
"rootPath": "govendor-example"
}
常用命令
govendor add +external
将已被引用且在 $GOPATH 下的所有包复制到 vendor 目录govendor add gopkg.in/yaml.v2
将指定包复制到 vendor 目录govendor list
列出代码中所有被引用到的包及其状态govendor list -v fmt
列出一个包被哪些包引用govendor fetch golang.org/x/net/context
从远程仓库添加或更新某个包(不会在 $GOPATH 也存一份)govendor fetch golang.org/x/net/context@v1
安装指定版本的包
还有很多,因为现在不怎么用了,不一一列举了,有需要可以去官网查看。
未解决的问题
无法精确的引用 外部包进行版本控制,不能指定引用某个特定版本的外部包,只是在开发时将其拷贝过来,但是一旦外部包升级,vendor 下面的包会跟着升级,而且 vendor 下面没有完整的引用包的版本信息, 对包升级带来了无法评估的风险。
Go modules
因为GOPATH
、Go Vendor
都存在很多问题,官方又推出了Go modules
。
首先,Go Modules
允许我们把项目创建到任意目录,这就解决了项目与依赖包混乱的问题。
go.mod
Go Modules
使用go.mod
文件来管理我们的项目,类似前端的package.json
文件。
下面是一个go.mod
文件的例子:
module github.com/nev/module1
go 1.19
require (
example.com/apple v0.1.2
example.com/banana v1.2.3
example.com/banana/v2 v2.3.4
example.com/pear // indirect
example.com/strawberry // incompatible
)
exclude example.com/banana v1.2.4
replace example.com/apple v0.1.2 => example.com/fried v0.1.0
replace example.com/banana => example.com/fish
module
是我们该项目包的名字。go
初始化项目时,使用的go版本。require
我们项目使用的依赖包及版本。exclude
跳过某个依赖库的版本,使用场景一般是我们知道某个版本有bug或者不兼容,为了安全起可以使用exclude跳过此版本。replace
将一个模块版本替换为另外一个模块版本。
go.sum
Go Modules
在做依赖管理时会创建两个文件,go.mod
和 go.sum
。
go.sum
防下载的依赖被恶意篡改,主要用于安全校验。
go.sum
记录了所有依赖的 module
的校验信息,在项目构建时,使用的依赖包必须跟 go.sum
中记录的完全一致, 否则就会报错。
- 在
go.sum
文件中会把直接依赖、间接依赖全部记录; - 项目中依赖的每一个包在
go.sum
中都会有两条记录,第一条是整个包所有文件一起计算得到哈希值,第二条表示该依赖包的go.mod
文件计算得到的哈希值。
# go.sum文件中每行记录由module名、版本和哈希组成,并由空格分开:
<module> <version> <hash>
<module> <version>/go.mod <hash>
比如,某个go.sum文件中记录了github.com/google/uuid 这个依赖包的v1.1.1版本的哈希值:
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
使用Go Modules
要想使用Go Modules
, 我们需要先在项目下执行go mod init 包名
,这样就会在项目下生成一个go.mod
文件。
go mod init example.com/myapp
执行完上面命令后,会在项目下生成一个go.mod
文件,内容如下:
module example.com/myapp
go 1.21
添加三方依赖
我们项目中,需要安装三方依赖时,可以使用go get
命令。
使用go modules
之后,go get
拉取依赖的方式就发生了变化。 注意:在go1.16之后,go get
只会下载依赖,不会进行安装了。
# 拉取最新的版本(优先择取 tag)
go get golang.org/x/text@latest
# 拉取 master 分支的最新 commit
go get golang.org/x/text@master
# 拉取 tag 为 v0.3.2 的 commit
go get golang.org/x/text@v0.3.2
# 拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:
go get golang.org/x/text@342b231
# 指定版本拉取,拉取v3版本
go get github.com/smartwalle/alipay/v3
# 更新依赖
go get -u github.com/smartwalle/alipay/v4
go get
会将依赖下载到$GOPATH/pkg/mod
目录下,同时会在go.mod
文件中添加依赖信息。
go mod download
go mod download
会根据go.mod
自动下载依赖到$GOPATH/pkg/mod
目录下,但不会更新go.mod
文件。
也可以下载 go.mod
中单个依赖 go mod download github.com/jinzhu/now
但不能下载 go.mod
中没有的依赖,否则会报错。
go mod tidy
go mod tidy
会自动检查依赖项,删除go.mod
存在,但项目中没用到依赖,并更新go.mod
文件。
还会自动下载项目中使用到,但还没有下载的依赖。
刚下载的go项目,可以使用该命令,自动下载依赖。
Go Modules 其他常用命令
go mod graph
查看当前项目的依赖树go mod edit
编辑go.mod文件go mod vendor
将依赖转移至本地的vendor文件go mod verify
检查本地依赖与项目依赖是否匹配
开启Go Modules
上面讲到了 Go modules
的使用,那如何开启Go modules
呢?
我们可以通过设置go env
中GO111MODULE
来开启Go modules
,该环境变量有三个值:off
、on
、auto
,默认值是auto
。
GO111MODULE=off
,无模块支持,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。意思就是,当我们编译执行一个go源码文件的时候,如果文件中有依赖其它包,它会首先去GOROOT指定的路径下查找,然后再去GOPATH指定的路径下查找。GO111MODULE=on
,模块支持。go命令行会使用modules,而一点也不会去GOPATH目录下查找。GO111MODULE=auto
,默认值。 若出现如下情况,则会使用Go modules
,否则使用GOPATH
方式。
# GO111MODULE=auto时,出现以下两种情况会启用Go modules:
1. 项目目录不在go path下
2. 当前或者上一级目录存在go.mod文件
设置代理
在一些网络较为特殊的环境下,我们若没办法直接访问库地址,我们可以通过代理去镜像拉取,通过go环境变量GOPROXY
来设置。
该值有2个参数: goProxyUrl,[off,direct]
- 若为
off
: 表示不会回源,即: 当代理拉取不到数据的时候,不会去源站镜像拉取。 - 若为
direct
: 表示会回源,当代理拉取不到的时候,会去源站尝试拉取
# 推荐使用七牛云goproxy.cn
go env -w GOPROXY=https://goproxy.cn,direct
设置私有库
有时候,我们自己的仓库不需要走代理,我们可以通过GOPRIVATE
来设置。
go env -w GOPRIVATE=git.mycompany.com,github.com/my/private
老项如何使用Go Modules
- 在项目下执行
go mod init 包名
,生成go.mod
文件 - 在项目下执行
go mod tidy
,自动下载依赖
Go Modules 好处
- 可以自动下载依赖包
- 项目不必放在GOPATH/src内了
- 项目内会生成一个go.mod文件,列出包依赖
- 所以来的第三方包会准确的指定版本号
- 对于已经转移的包,可以用replace 申明替换,不需要改代码
现在基本都是使用Go Modules
来管理项目了。
项目打包
项目部署前,需要先打包项目,我们需要使用go build
命令,该命令会将我们的go源代码编译生成二进制可执行文件。 默认将可执行文件输出到当前目录下。
也可以使用-o
参数指定输出目录与打包后的文件名。
# 将可执行文件输出到test目录下
go build -o ./test main.go
# 将可执行文件输出到test目录下,并命名为demo.exe
go build -o ./test/demo.exe main.go
go build 与 go install 的区别
go install
会将生成的二进制可执行文件,放到$GOBIN
目录下。go build
默认将可执行文件生成在当前目录下。go install
会生成库文件(.a
文件),go build
不会。
其他一些环境变量
GOROOT
go env
中 GOROOT
是Golang的安装目录,我们在终端执行go version
命令时,会去GOROOT目录下查找go的版本信息。
当我们程序使用导入系统库,例如: fmt的时候,其实会先到GOROOT
中进行寻找,假设GOROOT = /usr/local/go
,那么import fmt
其实该库的绝对路径为/usr/local/go/src/fmt/print.go
GOBIN
go env
中 GOBIN
是Golang的可执行文件目录,我们在终端执行go install
命令时,会将编译后的可执行文件放到GOBIN
目录下。
不设置的话,go env
查看为空,默认为GOPATH/bin
我们也可以把GOBIN
,加入到系统PATH环境变量中,方便我们随时执行里面的可执行文件。
go 常用命令
go run
编译并运行go代码,不会生成可执行文件,只会在临时目录下生成一个临时可执行文件,执行完毕后,会自动删除。 不需要设置GOPATH
go run main.go
go get
下载并安装依赖包到$GOPATH/pkg/mod
目录下, 会自动更新go.mod和go.sum文件。 不需要设置GOPATH
。
指定参数
-d # 只执行下载动作,而不执行安装动作
-fix # 在下载代码包后先执行修正动作,而后再进行编译和安装
-u # 利用网络来更新已有的代码包及其依赖包
-x # 显示过程
go get 变更记录
- 自 Go 1.17 起, 弃用
go get
命令安装可执行文件,使用go install
命令替代。
# Go 1.17 之前:go get 通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。实际分成两步操作:
# Go 1.17 之后: 弃用go get命令的编译和安装功能
# 1. 下载源码包
# 2. 执行 go install
- 自Go 1.18起,go get 命令不再有编译包的功能。将只有添加,更新,移除 go.mod 文件中的依赖项的功能。
- go get 命令将默认启用
-d
选项。
go env
查看go环境变量
# 环境变量配置文件路径
go env GOENV
# 列出所有环境变量
go env
# 列出所有环境变量(以json格式)
go env -json
# 修改某个环境变量
go env -w GOPROXY=https://goproxy.io,direct
# 重置某个变量
go env -u GOPROXY
go 其他常用命令
go list -m -versions github.com/gin-gonic/gin
查看可下载版本go list -m all
查看当前项目的所有依赖,也可以添加-json
参数,例如:go list -m -json all
go test
运行测试源码文件go build/test/run
这三个命令都会自动下载依赖,并更新go.mod
和go.sum
文件
完结撒花🎉🎉🎉,你又进步了一点点。
朋友,如果觉得本文章对你有帮助,可以点点赞支持一下嘛。先谢谢啦 ᕦ( •̀∀•́)ᕤ