深入浅出golangGoGolang 入门资料+笔记

golang快速入门[7.2]-北冥神功—go module绝技

2020-03-07  本文已影响0人  唯识相链2

前文

题记

前言

go module 诞生的背景 & 为什么需要go module

go module之前,有一些问题长期困扰go语言的开发人员

解决import 路径问题

import "github.com/gobuffalo/buffalo"

实际引用的是$GOPATH/src/github.com/gobuffalo/buffalo 文件中的代码。

## go.mod01 module github.com/gobuffalo/buffalo02...06

在go.mod文件的第一行指定了模块名,模块名表示开发人员可以用此来引用当前代码仓库中任何package的路径名,以此来替代$gopath的路径。从而,代码仓库在任何位置都已经没有关系,因为Go工具可以使用模块文件的位置和模块名来解析代码仓库中的任何内部import

解决代码捆绑和版本控制

对于任何版本控制(VCS)工具,我们都能在任何代码提交点打上"tag"标记,如下所示:

image

go moudle 使用

Module缓存

为了加快构建程序的速度并快速切换、获取项目中依赖项的更新,Go维护了下载到本地计算机上的所有模块的缓存,缓存目前默认位于$GOPATH/pkg目录中。有go的提议希望能够自定义缓存的位置。
所在位置看上去如下所示:

go/├── bin├── pkg     ├── darwin_amd64     └── mod└── src

在mod目录下,我们能够看到模块名路径中的第一部分用作了模块缓存中的顶级文件夹

~/go/pkg/mod » ls -l                                                                                                                                                                                jackson@192drwxr-xr-x    6 jackson  staff    192  1 15 20:50 cachedrwxr-xr-x    7 jackson  staff    224  2 20 17:50 cloud.google.comdrwxr-xr-x    3 jackson  staff     96  2 18 12:03 git.apache.orgdrwxr-xr-x  327 jackson  staff  10464  2 28 00:02 github.comdrwxr-xr-x    8 jackson  staff    256  2 20 17:27 gitlab.followme.comdrwxr-xr-x    6 jackson  staff    192  2 19 22:05 go.etcd.io...

当我们打开一个实际的模块,例如github.com/nats-io,我们会看到与nats库有关许多模块

~/go/pkg/mod » ls -l github.com/nats-io                                                                                                                                                             jackson@192total 0dr-x------  24 jackson  staff   768  1 17 10:27 gnatsd@v1.4.1dr-x------  15 jackson  staff   480  2 17 22:22 go-nats-streaming@v0.4.0dr-x------  26 jackson  staff   832  2 19 22:05 go-nats@v1.7.0dr-x------  26 jackson  staff   832  1 17 10:27 go-nats@v1.7.2...

为了拥有一个干净的工作环境,我们可以用如下代码清空缓存区。但是请注意,在正常的工作流程中,是不需要执行如下代码的。

$ go clean -modcache

开始一个新的项目

$ cd $HOME$ mkdir mathlib$ cd mathlib                                                                                                                                                                                 jackson@192$ touch main.go
~/mathlib » go mod init github.com/dreamerjackson/mathlib
module github.com/ardanlabs/service#### 引入第三方模块go 1.13
package mainimport "github.com/dreamerjackson/mydiv"func main(){}

我们在代码片段中导入了为了讲解go moudle而特地的引入的packagegithub.com/dreamerjackson/mydiv,其进行简单的除法操作,同时又引入了另一个包github.com/pkg/errors。其代码如下图所示:

image.gif

如下图所示,在goland中我们可以看到导入的package 是红色的,因为此时在go module的缓存并不能找到此package。

image.gif

下载第三方模块

$ go mod tidygo: finding github.com/dreamerjackson/mydiv latestgo: downloading github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161go: extracting github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161

同时我们在go.mod中能够看到新增加了一行用于表示我们引用的依赖关系

module github.com/dreamerjackson/mathlibgo 1.13require github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161
## go.sumgithub.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161 h1:QR1fJ05yjzJ0qv1gcUS+gAe5Q3UU5Y0le6TIb2pcJpQ=github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161/go.mod h1:h70Xf3RkhKSNbUF8W3htLNJskYJSITf6AdEGK22QksQ=github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

使用第三方模块

package mainimport (    "fmt"    "github.com/dreamerjackson/mydiv")func main(){    res,_ :=mydiv.Div(4,2)    fmt.Println(res)}

手动更新第三方模块

require github.com/dreamerjackson/mydiv latest

或者

require github.com/dreamerjackson/mydiv master

获取复制commitId 到最后

require github.com/dreamerjackson/mydiv c9a7ffa8112626ba6c85619d7fd98122dd49f850

还有一种办法是在终端当前项目中,运行go get

go get github.com/dreamerjackson/mydiv

此时如果我们再次打开go.sum文件会发现,go.sum中不仅仅存储了直接和间接的依赖,还会存储过去的版本信息。

github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161 h1:QR1fJ05yjzJ0qv1gcUS+gAe5Q3UU5Y0le6TIb2pcJpQ=github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161/go.mod h1:h70Xf3RkhKSNbUF8W3htLNJskYJSITf6AdEGK22QksQ=github.com/dreamerjackson/mydiv v0.0.0-20200305090126-c9a7ffa81126/go.mod h1:h70Xf3RkhKSNbUF8W3htLNJskYJSITf6AdEGK22QksQ=github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

依赖移除

当我们不想在使用此第三方包时,可以直接在代码中删除无用的代码,接着执行

$ go mod tidy

会发现go.modgo.sum 一切又都空空如也~

go module 最小版本选择原理

什么是最小版本选择原理

> go list -m -versions github.com/dreamerjackson/mydivgithub.com/dreamerjackson/mydiv v1.0.0 v1.0.1 v1.0.2 v1.0.3

假设现在有两个模块A、B,都依赖模块D。其中

A -> D v1.0.1,B -> D v1.0.2

验证最小版本选择原理

## v1.0.1package mydivimport "github.com/pkg/errors"func Div(a int,b int) (int,error){    if b==0{        return 0,errors.Errorf("new error b can't = 0")    }    return a/b,nil}## v1.0.2package mydivimport "github.com/pkg/errors"func Div(a int,b int) (int,error){    if b==0{        return 0,errors.Errorf("new error b can't = 0")    }    return a/b,nil}
## 模块Bpackage divimport (    "github.com/dreamerjackson/mydiv")func Div(a int,b int) (int,error){    return mydiv.Div(a,b)}
package mainimport (    "fmt"    div "github.com/dreamerjackson/minidiv"    "github.com/dreamerjackson/mydiv")func main(){    _,err1:= mydiv.Div(4,0)    _,err2 := div.Div(4,0)    fmt.Println(err1,err2)}

当前的依赖关系如下:

当前模块 --> 模块D v1.0.2当前模块 --> 模块B --> 模块D v1.0.1
$ go run main.gov1.0.2 b can't = 0 v1.0.2 b can't = 0
~/mathlib » go list -m all | grep mydivgithub.com/dreamerjackson/mydiv v1.0.2

我们还可以通过使用go mod mhy z指令,查看在哪里引用了包github.com/dreamerjackson/mydiv

~/mathlib » go mod why github.com/dreamerjackson/mydiv# github.com/dreamerjackson/mydivgithub.com/dreamerjackson/mathlibgithub.com/dreamerjackson/mydiv

查看直接和间接模块的当前和最新版本

~/mathlib » go list -m -u all | column -t                                                                                                                                                           jackson@192go: finding github.com/dreamerjackson/minidiv latestgithub.com/dreamerjackson/mathlibgithub.com/dreamerjackson/minidiv  v0.0.0-20200305104752-fcd15cf402bbgithub.com/dreamerjackson/mydiv    v1.0.2                              [v1.0.3]github.com/pkg/errors              v0.9.1

更新直接和间接模块

go get -t -d -v  ./...
~/mathlib » go get -u -t -d -v ./...                                                                                                                                                                jackson@192go: finding github.com/dreamerjackson/minidiv latestgo: downloading github.com/dreamerjackson/mydiv v1.0.3go: extracting github.com/dreamerjackson/mydiv v1.0.3
~/mathlib » go list -m all | grep mydiv                                                                                                                                                             jackson@192github.com/dreamerjackson/mydiv v1.0.3

重置依赖关系

$ rm go.*$ go mod init <module name>$ go mod tidy

语义版本控制(semantic version)

image
A --> 模块B --> 模块D v1.0.0A --> 模块C --> 模块D v2.0.0
package mydivimport "github.com/pkg/errors"func Div(a int,b int) (int,error){    if b==0{        return 0,errors.Errorf("v2.0.0 b can't = 0")    }    return a/b,nil}
module github.com/dreamerjackson/mydiv/v2
package mainimport (    "fmt"    div "github.com/dreamerjackson/minidiv"    mydiv "github.com/dreamerjackson/mydiv/v2")func main(){    _,err1:= mydiv.Div(4,0)    _,err2 := div.Div(4,0)    fmt.Println(err1,err2)}
mathlib --> 直接引用mydiv v2mathlib --> 直接引用minidiv --> 间接引用mydiv v1

当我们运行代码之后,会发现两段代码是共存的

v2.0.0 b can't = 0 ::v1.0.1 b can't = 0

接着执行go list,模块共存,验证成功~

~/mathlib(master*) » go list -m all | grep mydivgithub.com/dreamerjackson/mydiv v1.0.1github.com/dreamerjackson/mydiv/v2 v2.0.1

模块镜像(Module Mirror)

模块镜像于2019年八月推出,是go官方1.13版本的默认系统。模块镜像是一个代理服务器,以帮助加快构建本地应用程序所需的模块的获取。代理服务器实现了基于REST的API,并根据Go工具的需求进行了设计。
模块镜像将会缓存已请求的模块及其特定版本,从而可以更快地检索将来的请求。一旦代码被获取并缓存在模块镜像中,就可以将其快速提供给世界各地的用户。

checksum数据库

checksum数据库也于2019八月推出,是可以用来防止模块完整性、有效性的手段。它验证特定版本的任何给定模块代码的正确性,而不管何人何时何地以及是如何获取的。Google拥有唯一的校验和数据库,但是可以通过私有模块镜像对其进行缓存。

go module 环境变量

有几个环境变量可以控制与模块镜像和checksum数据库有关的行为

$ go envGONOPROXY=""GONOSUMDB=""GOPRIVATE=""GOPROXY="https://proxy.golang.org,direct"GOSUMDB="sum.golang.org"

Athens搭建私有模块镜像

Athens是一个私有模块镜像,可以用于搭建私有模块镜像。使用私有模块镜像的一个原因是允许缓存公共模块镜像无法访问的私有模块。Athens项目提供了一个在Docker Hub上发布的Docker容器,因此不需要特殊的安装。

docker run -p '3000:3000' gomods/athens:latest

接下来,启动一个新的终端会话以运行Athens,为大家演示其用法。启动Athens服务并通过额外的参数调试日志(请确保系统已经安装并启动了docker)并有科学*上网的环境

$ docker run -p '3000:3000' -e ATHENS_LOG_LEVEL=debug -e GO_ENV=development gomods/athens:latestINFO[7:11AM]: Exporter not specified. Traces won't be exported2020-03-06 07:11:30.671249 I | Starting application at port :3000
$ export GOPROXY="http://localhost:3000,direct"$ rm go.*$ go mod init github.com/dreamerjackson/mathlib$ go mod tidy

在Athens日志中即可查看对应信息

INFO[7:39AM]: incoming request    http-method=GET http-path=/github.com/dreamerjackson/mydiv/@v/list http-status=200INFO[7:39AM]: incoming request    http-method=GET http-path=/github.com/dreamerjackson/minidiv/@v/list http-status=200INFO[7:39AM]: incoming request    http-method=GET http-path=/github.com/dreamerjackson/minidiv/@latest http-status=200

go module 优势

总结

在本文中,使用详细的实例讲解了go module 是什么,为什么需要,其最佳实践以及其实现原理。希望读者在这篇文章之后,能够回答我们在开头提出的问题

参考资料

喜欢本文的朋友欢迎点赞分享~

image

唯识相链启用微信交流群(Go与区块链技术)

欢迎加微信:ywj2271840211

上一篇下一篇

猜你喜欢

热点阅读