Go进阶15:go版本>1.12mod教程

Go进阶15:go版本>1.12mod教程

1.前言

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

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

到 go 1.12 会删除对 GOPATH 的支持,go get 命令也会变成只能获取模块,不能像现在这样直接获取一个裸包.

可以用环境变量 GO111MODULE 开启或关闭模块支持,它有三个可选值:off,on,auto,默认值是 auto.

  • GO111MODULE=off 无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包.
  • GO111MODULE=on 模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖.
  • GO111MODULE=auto 在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持.

在使用模块的时候,GOPATH 是无意义的,不过它还是会把下载的依赖储存在 $GOPATH/src/mod 中,也会把 go install 的结果放在 $GOPATH/bin 中.

2.升级安装go 1.12(linux)其他系统同理

cd ~;
# 使用国内官方安装包
wget https://golang.google.cn/doc/install?download=go1.12.1.linux-amd64.tar.gz
sudo rm -rf /usr/local/go;
sudo tar -C /usr/local -xzf go1.12.1.linux-amd64.tar.gz;
echo '设置环境变量vim  ~/.bashrc /etc/profile ...中任意一个';
echo '删除旧版的 export GOPATH=  行'
echo '添加 export PATH=$PATH:/usr/local/go/bin';
vim ~/.bashrc;
echo 'vim 命令 :wq';
echo '从新加载环境变量文件';
source ~/.bashrc && go version

3.Jetbrains Goland 快速配置(强烈推荐)

  • Preferences -> Go -> Go modules(vgo)
  • 如果还有标红的提示,点击 Sync packages

4.Go Module 初始化(新建)

创建我们的测试项目:demo

$ echo $GOPATH
D:\code\Go_Path
$ pwd
/d/code/Temp_Code/src/demo.go.modules
# 初始化 Module
$ go mod init demo
$ ls
go.mod

创建用于测试的 Go 源码文件:main.go

// 可以看到该文件只包含了一个依赖包:"github.com/sirupsen/logrus"

package main

import (
    "sync"
    "github.com/sirupsen/logrus"
)

type Cache struct {
    sync.Map
}

func main() {
    cache := Cache{Map:sync.Map{}}

    cache.Store("i", "1")
    cache.Store("j", "2")
    cache.Store("k", "3")

    cache.Range(func(key, value interface{}) bool {
    	logrus.Infoln(key, value)
    	return true
    })
}

3.1构建模块

当我们使用 go build,go test 以及 go list 时,Go 会自动更新 go.mod 文件,并且将依赖关系写入其中.

$ go build ./...
go: finding github.com/sirupsen/logrus v1.0.6
go: finding golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac
go: finding golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b
go: downloading github.com/sirupsen/logrus v1.0.6
go: downloading golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac
go: downloading golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b

可以看到 go 自动查找了依赖并完成下载,但是下载的依赖包并不是下载到了 $GOPATH 中,而是在 $GOPATH/pkg/mod 目录下,且多个项目可以共享缓存的 module

然后,我们来看看当前目录发生了什么

$ ls
demo.exe  go.mod  go.sum  main.go

# demo.exe 不用多说,是编译生成的可执行文件

# 在来看看 go.mod 里面的变化
$ cat go.mod
module demo

require (
        github.com/sirupsen/logrus v1.0.6
        golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac // indirect
        golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b // indirect
)
# Note: go.mod 中记录了我们项目的直接或间接引用的包

# 另外,多了一个 go.sum 文件,我们来看看里面是什么内容
$ cat go.sum
github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac h1:7d7lG9fHOLdL6jZPtnV4LpI41SbohIJ1Atq7U991dMg=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b h1:cmOZLU2i7CLArKNViO+ZCQ47wqYFyKEIpbGWp+b6Uoc=
golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

# Note: 在 go.sum 是我们直接引用的 package 和它自身需要的依赖的版本记录,go module 就是根据这些去找对应的 package 的.

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

3.1.1 Go Module 初始化(新建)

cd 到之前的GOPATH/src/项目目录

# 之前的项目package名称
PNAME=github.com/libragen/felix;
cd "/oldGOPATH/src/${PNAME}";
go mod init $PNAME;
go mod tidy

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

3.2设置Module GOPROXY代理

国内用户可以使用的两个GOPROXY;

  • 阿里云镜像代理: http://mirrors.aliyun.com/goproxy/
  • https://goproxy.io
  • https://goproxy.cn

现在,当您构建或运行您的应用时,Go 将会通过 GOPROXY 获取依赖,

1.3.1在 Linux 或 macOS 上面,需要运行下面命令:

# 启用 Go Modules 功能
export GO111MODULE=on
# 配置 GOPROXY 环境变量
export GOPROXY=https://goproxy.io

或者,可以把上面的命令写到 .bashrc 或 .bash_profile 文件当中.

1.3.2在 Windows 上,需要运行下面命令

# 启用 Go Modules 功能
$env:GO111MODULE=on
# 配置 GOPROXY 环境变量
$env:GOPROXY=https://goproxy.io

1.3.3go版本>1.13 GOPRIVATE 环境变量来控制哪些私有仓库和依赖

如果您使用的 Go 版本>=1.13, 您可以通过设置 GOPRIVATE 环境变量来控制哪些私有仓库和依赖(公司内部仓库)不通过 proxy 来拉取,直接走本地,设置如下:

go env -w GOPROXY=https://goproxy.io,direct
# 设置不走 proxy 的私有仓库,多个用逗号相隔
go env -w GOPRIVATE=*.corp.example.com

3.3(可选) 配置goland 关闭对GOPATH的兼容

点击菜单 file ->settings ->go (ctrl+shift+s)

关闭GOPATH 开启GOMODULE

5.编辑mod 来替代replace 解决少数情况包被墙问题(不建议使用)

不建议使用,因为现在可以使用GOPROXY代理解决这一问题

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

go.mod中添加replace选项使用代码镜像来下载包. 如果不知道包的版本号码 直接使用 latest 替代golang.org/x/mobile => github.com/golang/mobile latest

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

replace (
	golang.org/x/mobile => github.com/golang/mobile latest
	google.golang.org/genproto => github.com/google/go-genproto latest
)

在执行 go mod tidy 之后go mod 自动帮助您设置正确的版本 ~

完整go.mod文件

module github.com/libragen/felix

go 1.12

replace (
	cloud.google.com/go => github.com/googleapis/google-cloud-go v0.37.2
	go.opencensus.io => github.com/census-instrumentation/opencensus-go v0.20.0
	go.uber.org/atomic => github.com/uber-go/atomic v1.3.2
	go.uber.org/multierr => github.com/uber-go/multierr v1.1.0
	go.uber.org/zap => github.com/uber-go/zap v1.9.1
	golang.org/x/build => github.com/golang/build v0.0.0-20190403045414-85a73d7451e7

	golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190325154230-a5d413f7728c
	golang.org/x/exp => github.com/golang/exp v0.0.0-20190402192236-7fd597ecf556
	golang.org/x/image => github.com/golang/image v0.0.0-20190321063152-3fc05d484e9f
	golang.org/x/lint => github.com/golang/lint v0.0.0-20190313153728-d0100b6bd8b3
	golang.org/x/mobile => github.com/golang/mobile v0.0.0-20190327163128-167ebed0ec6d
	golang.org/x/net => github.com/golang/net v0.0.0-20190328230028-74de082e2cca
	golang.org/x/oauth2 => github.com/golang/oauth2 v0.0.0-20190402181905-9f3314589c9a
	golang.org/x/perf => github.com/golang/perf v0.0.0-20190312170614-0655857e383f
	golang.org/x/sync => github.com/golang/sync v0.0.0-20190227155943-e225da77a7e6
	golang.org/x/sys => github.com/golang/sys v0.0.0-20190402142545-baf5eb976a8c
	golang.org/x/text => github.com/golang/text v0.3.0
	golang.org/x/time => github.com/golang/time v0.0.0-20190308202827-9d24e82272b4
	golang.org/x/tools => github.com/golang/tools v0.0.0-20190402200628-202502a5a924
	google.golang.org/api => github.com/googleapis/google-api-go-client v0.3.0
	google.golang.org/appengine => github.com/golang/appengine v1.5.0
	google.golang.org/genproto => github.com/google/go-genproto v0.0.0-20190401181712-f467c93bbac2
	google.golang.org/grpc => github.com/grpc/grpc-go v1.19.1
	gopkg.in/alecthomas/kingpin.v2 => github.com/alecthomas/kingpin v2.2.6+incompatible
	gopkg.in/mgo.v2 => github.com/go-mgo/mgo v0.0.0-20180705113738-7446a0344b78
	gopkg.in/tomb.v1 => github.com/go-tomb/tomb v0.0.0-20141024135613-dd632973f1e7
	gopkg.in/vmihailenco/msgpack.v2 => github.com/vmihailenco/msgpack v4.0.4+incompatible
	gopkg.in/yaml.v2 => github.com/go-yaml/yaml v2.1.0+incompatible
	labix.org/v2/mgo => github.com/go-mgo/mgo v0.0.0-20180705113738-7446a0344b78
	launchpad.net/gocheck => github.com/go-check/check v0.0.0-20180628173108-788fd7840127
)

require (
	github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 // indirect
	github.com/denisenkom/go-mssqldb v0.0.0-20190313032549-041949b8d268 // indirect
	github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
	github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect
	github.com/gin-gonic/gin v1.3.0
	github.com/go-ole/go-ole v1.2.4 // indirect
	github.com/gofrs/uuid v3.2.0+incompatible // indirect
	github.com/golang/protobuf v1.3.1
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
	github.com/jinzhu/gorm v1.9.2
	github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
	github.com/jinzhu/now v1.0.0 // indirect
	github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 // indirect
	github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07
	github.com/json-iterator/go v1.1.6 // indirect
	github.com/kr/pretty v0.1.0 // indirect
	github.com/lib/pq v1.0.0 // indirect
	github.com/mattn/go-isatty v0.0.7
	github.com/mattn/go-runewidth v0.0.4 // indirect
	github.com/mattn/go-sqlite3 v1.10.0
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.1 // indirect
	github.com/olekukonko/tablewriter v0.0.1
	github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829
	github.com/prometheus/common v0.2.0
	github.com/prometheus/node_exporter v0.17.0
	github.com/shirou/gopsutil v2.18.12+incompatible
	github.com/sirupsen/logrus v1.4.0
	github.com/spf13/cobra v0.0.3
	github.com/spf13/viper v1.3.2
	golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
	golang.org/x/net v0.0.0-20190311183353-d8887717615a
	google.golang.org/grpc v1.19.0
	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
	gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
	gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
)

6.go mod命令

download    download modules to local cache (下载依赖的module到本地cache))
edit        edit go.mod from tools or scripts (编辑go.mod文件)
graph       print module requirement graph (打印模块依赖图))
init        initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy        add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor      make vendored copy of dependencies (将依赖复制到vendor下)
verify      verify dependencies have expected content (校验依赖)
why         explain why packages or modules are needed (解释为什么需要依赖)

7.go get 升级

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

8.go mod vendor

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

目录