Go mod:一文教你真正用起来Go Module依赖管理

Golang环境变量

  1. GOROOT:go的安装路径

    ~/.bash_profile中添加下面语句配置GOROOT环境变量

    GOROOT=/usr/local/go
    export GOROOT
    

    要执行go命令和go工具, 就要配置go的可执行文件的路径:

    export $PATH:$GOROOT/bin
    

    注:$PATH windows用;符号分割, mac和类unix用:符号分割

  2. GOPATH: go的工作路径

    可以在自己的用户目录下面创建一个目录, 如gopath

    cd ~
    mkdir gopath
    

    ~/.bash_profile中添加如下语句:

    export GOPATH=/Users/username/gopath
    

    不要把GOPATH设置成go的安装路径, GOPATH下主要包含三个目录:bin pkg src

    • src: 存放源代码(比如:.go .c .h .s等)

    • pkg: 编译后生成的文件(比如:.a)

    • bin: 编译后生成的可执行文件, 为了方便,可以把此目录加入到 $PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录

    注: Go 1.8 版本之前,GOPATH 环境变量默认是空的;1.8版本之后,默认路径是:$HOME/go

Go Modules

go modules是 golang 1.11引入的新特性。模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。

  1. GO111MODULE有三个值:off、on 和 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. GOPROXY

    由于中国政府的网络监管系统,Go 生态系统中有着许多中国 Gopher 们无法获取的模块,比如最著名的 golang.org/x/...。并且在中国大陆从 GitHub 获取模块的速度也有点慢。因此需要配置GOPROXY来加速Module依赖下载,这里使用goproxy.cn代理,详细介绍:传送门

    • Go 1.13及以上版本

      go env -w GOPROXY=https://goproxy.cn,direct
      
    • Go 1.13以下的版本

      export GOPROXY=https://goproxy.cn
      

    注: 推荐将 GO111MODULE 设置为on 而不是auto

Go mod

Golang 1.11 版本引入的 go mod ,其思想类似maven:摒弃vendor和GOPATH,拥抱本地库。从 Go 1.11 开始,Go 允许在 $GOPATH/src 外的任何目录下使用 go.mod 创建项目。在$GOPATH/src中,为了兼容性,Go 命令仍然在旧的 GOPATH 模式下运行。从 Go 1.13 开始,Module模式将成为默认模式。

  1. go mod 命令

    go mod
    The commands are:
      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 (解释为什么需要依赖)
    
  2. 新项目

    你可以在GOPATH之外创建新的项目。

    go mod init packagename可以创建一个空的go.mod,然后你可以在其中增加require github.com/smallnest/rpcx latest依赖,或者像上面一样让go自动发现和维护。

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

  3. 老项目

    假设你已经有了一个go 项目, 比如在$GOPATH/github.com/smallnest/rpcx下, 你可以使用go mod init github.com/smallnest/rpcx在这个文件夹下创建一个空的go.mod (只有第一行 module github.com/smallnest/rpcx)。

    然后你可以通过 go get ./...让它查找依赖,并记录在go.mod文件中(你还可以指定 -tags,这样可以把tags的依赖都查找到)。

    通过go mod tidy也可以用来为go.mod增加丢失的依赖,删除不需要的依赖,但是我不确定它怎么处理tags

    执行上面的命令会把go.modlatest版本换成实际的最新的版本,并且会生成一个go.sum记录每个依赖库的版本和哈希值。

实战

示例一:创建一个新项目

  1. GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件

➜  ~ mkdir hello
➜  ~ cd hello
➜  hello go mod init hello
go: creating new go.mod: module hello
➜  hello ls
go.mod
➜  hello cat go.mod
module hello

go 1.12

go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。

go.mod 提供了module, requirereplaceexclude 四个命令

  • module 语句指定包的名字(路径)

  • require 语句指定的依赖项模块

  • replace 语句可以替换依赖项模块

  • exclude 语句可以忽略依赖项模块

  1. 添加依赖

新建一个 server.go 文件,写入以下代码:

package main

import (
	"net/http"
	
	"github.com/labstack/echo"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":1323"))
}

执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载:

$ go run server.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: extracting github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding github.com/labstack/gommon v0.2.8
# 此处省略很多行
...

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1323

现在查看go.mod 内容:

$ cat go.mod

module hello

go 1.12

require (
	github.com/labstack/echo v3.3.10+incompatible // indirect
	github.com/labstack/gommon v0.2.8 // indirect
	github.com/mattn/go-colorable v0.1.1 // indirect
	github.com/mattn/go-isatty v0.0.7 // indirect
	github.com/valyala/fasttemplate v1.0.0 // indirect
	golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)

go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit。 go 会自动生成一个 go.sum 文件来记录 dependency tree:

$ cat go.sum
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
... 省略很多行
  1. 再次执行脚本 go run server.go 发现跳过了检查并安装依赖的步骤。

  2. 可以使用命令 go list -m -u all 来检查可以升级的package,使用go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod * 也可以使用 go get -u 升级所有依赖

go get 升级

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

  • 运行 go get -u=patch 将会升级到最新的修订版本

  • 运行 go get package@version 将会升级到指定的版本号version

  • 运行go get如果有版本的更改,那么go.mod文件也会更改

示例二:改造现有项目(helloword)

项目目录为:

$ tree
.
├── api
│   └── apis.go
└── server.go

1 directory, 2 files

server.go 源码为:

package main

import (
    api "./api"  // 这里使用的是相对路径
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", api.HelloWorld)
    e.Logger.Fatal(e.Start(":1323"))
}

api/apis.go 源码为:

package api

import (
    "net/http"

    "github.com/labstack/echo"
)

func HelloWorld(c echo.Context) error {
    return c.JSON(http.StatusOK, "hello world")
}

export GO111MODULE=on 先设置环境变量开启Module功能

  1. 使用 go mod init *** 初始化go.mod

$ go mod init helloworld
go: creating new go.mod: module helloworld
  1. 运行 go run server.go

go: finding github.com/labstack/gommon/color latest
go: finding github.com/labstack/gommon/log latest
go: finding golang.org/x/crypto/acme/autocert latest
go: finding golang.org/x/crypto/acme latest
go: finding golang.org/x/crypto latest
build command-line-arguments: cannot find module for path _/home/gs/helloworld/api

首先还是会查找并下载安装依赖,然后运行脚本 server.go,这里会抛出一个错误:

build command-line-arguments: cannot find module for path _/home/gs/helloworld/api

但是go.mod 已经更新:

$ cat go.mod
module helloworld

go 1.12

require (
        github.com/labstack/echo v3.3.10+incompatible // indirect
        github.com/labstack/gommon v0.2.8 // indirect
        github.com/mattn/go-colorable v0.1.1 // indirect
        github.com/mattn/go-isatty v0.0.7 // indirect
        github.com/valyala/fasttemplate v1.0.0 // indirect
        golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
)

那为什么会抛出这个错误呢?

这是因为 server.go 中使用 internal package 的方法跟以前已经不同了,由于 go.mod会扫描同工作目录下所有 package 并且变更引入方法,必须将 helloworld当成路径的前缀,也就是需要写成 import helloworld/api,以往 GOPATH/dep 模式允许的 import ./api 已经失效,详情可以查看这个 issue

  1. 更新旧的package import 方式

所以server.go 需要改写成:

package main

import (
    api "helloworld/api"  // 这是更新后的引入方法
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", api.HelloWorld)
    e.Logger.Fatal(e.Start(":1323"))
}

一个小坑:开始在golang1.11 下使用go mod 遇到过 go build github.com/valyala/fasttemplate: module requires go 1.12 这种错误,遇到类似这种需要升级到1.12 的问题,直接升级golang1.12 就好了。幸亏是在1.12 发布后才尝试的go mod 🤷‍♂️

  1. 到这里就和新创建一个项目没什么区别了

总结

Go Module是Go依赖管理的未来。 从1.11之后开始支持该功能,随着Go依赖管理的功能增强,以后再也不用被现在的包管理犯难了。

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值