GO语言学习之Go Module

GO语言学习之Go Module

1. 包管理方式变迁

最初,Go的包管理工具依赖于GOPATH或者vender目录,例如:

  • govender
  • dep
  • glide
  • godep

Go 1.11时,官方推出了版本管理工具go module,并且从1.13开始,将其作为Go语言默认的依赖管理工具。

2. 使用介绍

2.1 环境变量设置

国内使用Go语言,必须配置Proxy,否则很多库依赖是访问不到的:

set GOPROXY=https://goproxy.cn

其次是GO111MODULE,通过它可以选择开启或者关闭模块支持,它主要有三个可选值:off、on、auto,默认值是auto:

  • GO111MODULE=off 禁用模块支持,编译项目时,会从GOPATH和vender文件夹中查找包
  • GO111MODULE=on 启用模块支持,编译时会忽略GOPATH和vender目录,只根据go.mod文件中的依赖信息下载、寻找依赖,依赖文件默认会存储在GOPATH/pkg/mod目录下。
  • GOPATH111MODULE=auto 将会根据项目目录下是否有go.mod文件、vender目录自动选择当前模式

2.2 go mod

命令行输入go mod help,会提示相关的指令;

download    download modules to local cache
edit        edit go.mod from tools or scripts
graph       print module requirement graph
init        initialize new module in current directory
tidy        add missing and remove unused modules
vendor      make vendored copy of dependencies
verify      verify dependencies have expected content
why         explain why packages or modules are needed

此外go.mod文件会记录当前模块的依赖信息,举个例子:

module icode.baidu.com/baidu/personal-code/dingjiefeng

go 1.14

require (
    golang.org/x/net v0.0.0-20200930145003-4acb6c075d10
    gopkg.in/gcfg.v1 v1.2.3
    gopkg.in/warnings.v0 v0.1.2 // indirect
    icode.baidu.com/baidu/go-lib/http-util2 v0.0.0-20200323031414-9bb6decc3fae
    icode.baidu.com/baidu/go-lib/log v0.0.0-20200715063458-8f6bd80f9579
    icode.baidu.com/baidu/go-lib/web-monitor v0.0.0-20200312080700-a72e63185eba // indirect
)

mod文件里有几个关键字,含义如下:

  • module 定义包名
  • require 定义依赖包以及对应版本
  • replace 替换到本地实际路径

2.3 使用module的实际流程

我们可以walk through一下使用go module的实际流程:

2.3.1 创建一个新的模块

GOPATH/src目录外新建文件夹test-mod,并新建文件hello.go

package main

import "fmt"

func main() {
        fmt.Println("vim-go")
}

在该目录下,运行命令行 go mod init djf.com/m/v0,运行后可以发现,目录下,多了文件go.mod

module djf.com/m/v0

go 1.13

2.3.2 添加依赖

对于开发者来说,包管理的主要功能就是使用别人的代码模块,这里我们在代码中引入一个第三方的包:

package main

import (
        "fmt"
        "rsc.io/quote"
)

func Hello() string {
        return quote.Hello()
}

func main() {
        fmt.PrintLn(Hello())
        fmt.Println("vim-go")
}

直接运行go run hello.go,输出:

djf@BDSY132990:/mnt/e/projects/go/test-mod$ go run hello.go
Ahoy, world!
vim-go

再看看mod文件,已经自动更新了对应的依赖信息:

module djf.com/m/v0

go 1.13

require rsc.io/quote v1.5.2 // indirect

依赖模块会自动被下载到GOPATH/pkg/mod目录下,通常,引入一个包,可能会间接引入其他的包,可以通过go list -m all命令查看目前项目依赖的所有包信息。

2.3.3 升级依赖

更改依赖包的信息,直接修改import中的包信息,然后重新运行就可以。可以使用go get 命令手动获取新的依赖。

2.3.4 去除冗余依赖

使用go mod tidy命令,可以将已经不再使用的依赖去除。

3. 一些实际使用问题

3.2 引入本地包

通常我们可以通过github发布自己开发的模块,然后通过使用类似github.com/dingjiefeng/{moduleName}/{tag}的方式import进来;这个前提是本地的module已经被推送到了线上,如果想访问本地的module怎么办呢?
可以使用前面提到的replace关键字;手动修改go.mod文件,例如下面:

module main
require {
    moduleTest v0.0.0
}

replace {
    moduleTest => /mnt/e/codes/go/Test
}

之后直接import "moduleTest"就会自动访问本地路径中的代码模块。

3.3 引入私有仓库的包

在企业内部,经常会引用一些企业私有仓库的包。之前,我们设置了GOPROXY,作为寻找包的访问入口,但是很明显,这种方式是访问不到私有仓库的。

GO 1.3以后,go语言还提供了一套基于环境变量的机制,设置在某些情况下,绕过proxy,通过git ssh的方式直接访问企业私有库的包。具体操作:

  1. 设置环境变量GOPRIVATEGONOPROXY, 例如GOPRIVATE=*.baidu.com,GONOPROXY=**.baidu.com**GOPRIVATE表示识别私有包名的正则表达式,GONOPROXY表示不走proxy方式的包名的正则式。
  2. 修改~/.gitconfig文件,使得git访问私有仓库时,使用ssh方式;例如
    [user]
        name = xxx
        email = xxx@baidu.com
    [url "git@icode.baidu.com:"]
        insteadOf = https://icode.baidu.com/

这样设置后,如果我们在代码中,引用私有仓库中的包,例如:

import (
    httpUtil "icode.baidu.com/baidu/go-lib/http-util2"
)

运行go rungo get go mod tidy命令同步本地包时,会利用git ssh的方式自动从私有仓库获取包,并更新go.mod文件:

require (
    icode.baidu.com/baidu/go-lib/http-util2
)

4. 总结

go的包管理方式总体上感觉还是挺难用的,通过环境变量的方式来设置一点儿也不便利。

个人经验:如果遇到无法找到包的情况,建议仔细看下报错信息,通常在报错信息里,会说明无法获取的具体原因,例如私有库的包,正确设置环境变量后,报错信息里面按理说是不会有任何关于GOPROXY的内容的,如果提示是在proxy下找不到、没权限,基本可以认为是设置没有生效或者设置错误了。

发表评论