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

weishixianglian · 2020年03月26日 · 269 次阅读

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

前文

题记

  • 《庄子》‘逍遥游’ 有云:且夫水之积也不厚,则其负大舟也无力。覆杯水于坳堂之上,则芥为之舟;置杯焉则胶,水浅而舟大也。
  • 我们今天要介绍的就是北冥神功—go module 绝技,以吸收他人内力为己所用。而且别人的内力愈强 吸力愈大。极天下代码于一身,好不快活。

前言

  • 在上文中,我们介绍了gopath的含义、功能、优劣、以及如何通过 GOPATH 来组织项目
  • 在本文中,我们讲介绍go module的原理和用法以试图能够回答下面的几个问题
    • go module是什么?
    • go module为什么需要?
    • go module的基本使用方法是什么?
    • go module如何管理版本与依赖?
    • go module如何解决依赖的冲突问题?
    • go module 环境变量的配置与使用方式?
    • 如何搭建私有module镜像? ## go module诞生的背景 & 为什么需要go modulego module之前,有一些问题长期困扰 go 语言的开发人员
  • 能否将 go 工程代码脱离gopath之外
  • 能否处理版本依赖问题并且自动选择最兼容的依赖版本
  • 能否使用 go 工具本地管理依赖项,自定义依赖项
  • go1.11 开始支持,go1.13 全面支持的go modules 正是为了解决上面的问题诞生的,下面我们详细介绍go module企图解决的问题 #### 解决import 路径问题
  • 在介绍gopath时,我们介绍了如果导入为
import "github.com/gobuffalo/buffalo"

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

  • 也就是说,在gopath中 ,导入路径与项目在文件系统中的目录结构和名称必须是匹配的。
  • 那么能否import 路径为github.com/gobuffalo/buffalo,但是项目实际的路径却是在另一个任意的文件目录中?(例如/users/gobuffalo/buffalo).答案是肯定的,go module 通过在一个特殊的叫做go.mod的文件中指定模块名来解决这一问题。
## go.mod
01 module github.com/gobuffalo/buffalo
02
...
06

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

解决代码捆绑和版本控制

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

  • 使用 VCS 工具,开发人员可以通过引用特定标签将软件包的任何特定版本克隆到本地。

  • 当我们引用一个第三方包时,可能并不总是希望应用项目最新的代码,而是某一个特定与当前项目兼容的代码。对于某一个项目来说,可能并没有意识到有人在使用他们的代码,或者某种原因进行了巨大的不兼容更新。

  • 我们希望能够指明需要使用的第三方包的版本,并且 go 工具能够方便下载、管理

  • 更棘手的是,一个第三方包 A 可能引用了其他的第三方包 B,因此还必须把第三方包 A 的全部依赖下载

    • 如何查找并把所有的依赖包下载下来?
    • 某一个包下载失败应该怎么办?
    • 所有项目之间如何进行依赖的传导?
    • 如何选择一个最兼容的包?
    • 如何解决包的冲突?
    • 如果希望在项目中同时引用第三方包的二个不同版本,需要如何处理?
  • 因此,只通过gopath维护单一的 master 包的方式是远远不够的,因为依赖包的最新代码不一定与项目兼容。尽管 go 社区已经针对以上问题提供了一些解决方案(例如 dep,godep,glide 等)但是 go 官方的go moudle提供了一种集成解决方案,通过在文件中维护直接和间接依赖项的版本列表来解决这一问题。通过将一个特定版本的依赖项看做是捆绑的不可变的依赖项,就叫做一个模块(moudle)

go moudle 使用

Module 缓存

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

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

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

~/go/pkg/mod » ls -l                                                                                                                                                                                jackson@192
drwxr-xr-x    6 jackson  staff    192  1 15 20:50 cache
drwxr-xr-x    7 jackson  staff    224  2 20 17:50 cloud.google.com
drwxr-xr-x    3 jackson  staff     96  2 18 12:03 git.apache.org
drwxr-xr-x  327 jackson  staff  10464  2 28 00:02 github.com
drwxr-xr-x    8 jackson  staff    256  2 20 17:27 gitlab.followme.com
drwxr-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@192
total 0
dr-x------  24 jackson  staff   768  1 17 10:27 gnatsd@v1.4.1
dr-x------  15 jackson  staff   480  2 17 22:22 go-nats-streaming@v0.4.0
dr-x------  26 jackson  staff   832  2 19 22:05 go-nats@v1.7.0
dr-x------  26 jackson  staff   832  1 17 10:27 go-nats@v1.7.2
...

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

$ go clean -modcache

开始一个新的项目

  • 我们从GOPATH外开始一个新的项目讲解,新建一个新建夹以及一个main文件
$ cd $HOME
$ mkdir mathlib
$ cd mathlib                                                                                                                                                                                 jackson@192
$ touch main.go
  • 接着在当前目录中,执行如下指令初始化 moudle。
~/mathlib » go mod init github.com/dreamerjackson/mathlib
  • go mod init指令的功能很简单,自动生成一个go.mod文件 后面紧跟的路径即是自定义的模块名。习惯上以托管代码仓库的 URL 为模块名(代码将会放置在https://github.com/dreamerjackson/mathlib下)
  • go.mod文件 位于项目的根目录下,内容如下所示,第一行即为模块名。
module github.com/ardanlabs/service

#### 引入第三方模块
go 1.13
  • 接下来我们将书写初始化的代码片段
package main

import "github.com/dreamerjackson/mydiv"

func main(){

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

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

下载第三方模块

  • 为了能够将此 package 下载到本地,我们可以使用go mod tidy指令
$ go mod tidy
go: finding github.com/dreamerjackson/mydiv latest
go: downloading github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161
go: extracting github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161

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

module github.com/dreamerjackson/mathlib

go 1.13

require github.com/dreamerjackson/mydiv v0.0.0-20200305082807-fdd187670161

  • 注意在这里间接的依赖(即github.com/dreamerjackson/mydiv 依赖的github.com/pkg/errors)并没有也没有必要在go.mod文件展示出来,而是出现在了一个自动生成的新的文件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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

使用第三方模块

  • 接着就可以愉快的调用我们的代码了
package main

import (
    "fmt"
    "github.com/dreamerjackson/mydiv"
)

func main(){
    res,_ :=mydiv.Div(4,2)
    fmt.Println(res)
}
  • 运行go run 命令后,即会为我们输出除法结果2

手动跟新第三方模块

  • 假设我们依赖的第三方包出现了跟新怎么办?如果将依赖代码更新到最新的版本呢?
  • 有多种方式可以实现依赖模块的更新,在go.mod文件中修改版本号为:
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 mod tidy即会进行更新

此时如果我们再次打开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 决定采用其他方法,Russ Cox 花费了大量时间和精力撰写和谈论 Go 团队的版本选择方法,即最小版本选择 (Minimal Version Selection,MVS)。从本质上讲,Go 团队相信 MVS 可以为 Go 程序提供最佳的机会,以实现兼容性和可重复性。我建议阅读这篇文章,以了解 Go 团队为什么相信这一点。 #### 什么是最小版本选择原理
  • go 最小版本选择指的是选择项目中最合适的最小版本。并不是说 MVS 不能选择最新的版本,而是如果项目中任何依赖不需要最新的版本,则不需要它。
  • 举一个简单的例子,假设现在项目github.com/dreamerjackson/mydiv的最新版本为v1.0.2,可通过下面指令查看所有
> go list -m -versions github.com/dreamerjackson/mydiv
github.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
  • 如果我们的当前项目只依赖 A,这个时候go module会如何选择呢?像 dep 这样的依赖工具将选择 v1.0.3,即最新的语义版本控制。但是在go module中,最小版本选择原理将遵循 A 项目声明的版本,即 v1.0.1
  • 如果随后当前项目又引入了模块 B 的新代码怎么办?将模块 B 导入项目后,Go 会将项目的模块 D 版本从 v1.0.1 升到 v1.0.2。为模块 D 的所有依赖项(模块 A 和 B)选择模块 D 的 “最小” 版本,该版本当前处于所需版本集(v1.0.1 和 v.1.0.2)中
  • 最后,如果删除刚刚为模块 B 添加的代码,会发生什么?Go 会将项目锁定到模块 D 的版本 v1.0.2 中。降级到版本 v1.0.1 将是一个更大的更改,而 Go 知道版本 v1.0.1 可以正常运行并且稳定,因此版本 v1.0.2 仍然是 “最新版本”。 #### 验证最小版本选择原理
  • 为了验证最小版本选择原理,作者呕心沥血设计了一个简单的示例。 以项目github.com/dreamerjackson/mydiv为例,读者可以将其看做上节中的模块D,其v1.0.1v1.0.2版本的代码如下,只是简单的改变了错误返回的字符串。
## v1.0.1
package mydiv
import "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.2
package mydiv
import "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
}
  • 接着模块Bgithub.com/dreamerjackson/minidiv 引用了模块 D 即github.com/dreamerjackson/mydiv v1.0.1 版本
## 模块B
package div

import (
    "github.com/dreamerjackson/mydiv"
)

func Div(a int,b int) (int,error){
    return mydiv.Div(a,b)
}
  • 最后当前的项目,我们将其称为模块Now直接依赖了模块 D v1.0.2,同时依赖了模块 B
  • 当前代码如下:
package main

import (
    "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
  • 因此我们将验证,是否和我们所料,当前项目选择了模块 D v1.0.2 呢?
  • 验证方式有两种:第一种为直接运行,查看项目采用了哪一个版本的代码
$ go run main.go
v1.0.2 b can't = 0 v1.0.2 b can't = 0
  • 如上所示,输出的结果全部是我们在模块 D v1.0.2 中定义的代码!
  • 第二种方式是使用go list指令
~/mathlib » go list -m all | grep mydiv
github.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/mydiv
github.com/dreamerjackson/mathlib
github.com/dreamerjackson/mydiv

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

  • 我们可以使用go list -m -u all 指令查看直接和间接模块的当前和最新版本
~/mathlib » go list -m -u all | column -t                                                                                                                                                           jackson@192
go: finding github.com/dreamerjackson/minidiv latest
github.com/dreamerjackson/mathlib
github.com/dreamerjackson/minidiv  v0.0.0-20200305104752-fcd15cf402bb
github.com/dreamerjackson/mydiv    v1.0.2                              [v1.0.3]
github.com/pkg/errors              v0.9.1
  • 如上所示,我们可以看到github.com/dreamerjackson/mydiv的当前版本为v1.0.2,但是最新的版本为v1.0.3

更新直接和间接模块

  • 获取直接和间接模块可以使用go get指令。其中有不少的参数。
  • 下面命令以最小版本原则更新所有的直接和间接模块
go get -t -d -v  ./...
  • -t 考虑构建测试所需的模块
  • -d 下载每个模块的源代码
  • -v 提供详细输出
  • ./... 在整个源代码树中执行这些操作,并且仅更新所需的依赖项
  • 注意,除非你了解项目的所有细节,否则慎用全部的最大最新版本的跟新

  • 如果 go get 中使用-u参数会用最大最新版本原则更新所有的直接和间接模块

~/mathlib » go get -u -t -d -v ./...                                                                                                                                                                jackson@192
go: finding github.com/dreamerjackson/minidiv latest
go: downloading github.com/dreamerjackson/mydiv v1.0.3
go: extracting github.com/dreamerjackson/mydiv v1.0.3
  • 接着我们可以再次查看当前引用的版本,我们会发现模块github.com/dreamerjackson/mydiv已经强制更新到了最新的 v1.0.3
~/mathlib » go list -m all | grep mydiv                                                                                                                                                             jackson@192
github.com/dreamerjackson/mydiv v1.0.3

重置依赖关系

  • 如果您不满意所选的模块和版本,则始终可以通过删除go.mod go.sum模块文件并再次运行 go mod tidy 来重置。当项目还不太成熟时这是一种选择。
$ rm go.*
$ go mod init <module name>
$ go mod tidy

语义版本控制(semantic version)

  • Go 模块引入了一种新的导入路径语法,即语义导入版本控制。每个语义版本均采用 vMAJOR.MINOR.PATCH 的形式。
    • MAJOR 主版本号,如果有大的版本更新,导致 API 和之前版本不兼容。我们遇到的就是这个问题。
    • MINOR 次版本号,当你做了向下兼容的新 feature。
    • PATCH 修订版本号,当你做了向下兼容的修复 bug fix。
    • v 所有版本号都是 v 开头。
  • 如果两个版本具有相同的主编号,则预期更高版本(如果您愿意,更大版本)将与较早版本(较小版本)向后兼容。但是,如果两个版本的主要编号不同,则它们之间没有预期的兼容性关系。
  • 因此我们在上面的实例中可以看到,go 预料到 v1.0.3 与 v1.0.1 是兼容的,因为他们有相同的主版本号1。但是一般我们将版本升级到了v2.0.0,即被认为是出现了重大的更新。

  • 如上图实例显示了 go 对于版本跟新的处理。my/thing/v2标识特定模块的语义主版本2。版本 1 是my/thing,模块路径中没有明确的版本。但是,当您引入主要版本 2 或更大版本时,必须在模块名称后添加版本,以区别于版本 1 和其他主要版本,因此版本 2 为 my/thing/v2,版本 3 为 my/thing/v3,依此类推。

  • 假设模块 A 引入了模块 B 和模块 C,模块 B 引入了模块 Dv1.0.0,模块 C 引入了模块 Dv2.0.0。则看见来就像是

A --> 模块B --> 模块D v1.0.0
A --> 模块C --> 模块D v2.0.0
  • 由于 v1 和 v2 模块的路径不相同,因此他们之间会是互不干扰的两个模块。
  • 下面我们用实例来验证
  • 首先我们给 mydiv 打一个 v2.0.0 的 tag,其代码如下,简单修改了错误文字v2.0.0 b can't = 0
package mydiv
import "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
}
  • 同时需要修改 v2 模块路径名为:
module github.com/dreamerjackson/mydiv/v2
  • 接着在 mathlib 中,代码如下:
package main

import (
    "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 v2
mathlib --> 直接引用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 mydiv
github.com/dreamerjackson/mydiv v1.0.1
github.com/dreamerjackson/mydiv/v2 v2.0.1

模块镜像(Module Mirror)

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

checksum 数据库

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

go module 环境变量

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

  • GOPROXY:一组指向模块镜像的 URL,用于获取模块。如果您希望 Go 工具仅直接从 VCS 地址获取模块,则可以将其设置为direct。如果将此设置为off,则将不会下载模块
  • GOSUMDB:用于验证给定模块/版本的代码的 checksum 数据库地址。此地址用于形成一个适当的 URL,该 URL 告诉 Go 工具在哪里执行这些 checksum 校验和查找。该 URL 可以指向 Google 拥有的 checksum 数据库,也可以指向支持对 checksum 数据库进行缓存或代理的本地模块镜像。如果您不希望 Go 工具验证添加到 go.sum 文件中的给定模块/版本的哈希码,也可以将其设置为 off,仅在将任何新 module 行添加到 go.sum 文件之时,才查询 checksum 数据库
  • GONOPROXY:一组基于 URL 的模块路径,这些模块不会使用模块镜像来获取,而应直接在 VCS 地址上获取。
  • GONOSUMDB:一组基于 URL 的模块路径,这些模块的哈希码不会在 checksum 数据库中查找。
  • GOPRIVATE:一个方便变量,用于将 GONOPROXY 和 GONOSUMDB 设置为相同的默认值 我们可以通过go env 来查看到这些默认值
$ go env
GONOPROXY=""
GONOSUMDB=""
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOSUMDB="sum.golang.org"
  • 这些默认值告诉 Go 工具使用 Google 模块镜像和 Google checksum 数据库。如果这些 Google 服务可以访问您所需的所有代码,则建议使用此配置。如果 Google 模块镜像恰好以 410(消失)或 404(未找到)响应,则使用 direct(这是 GOPROXY 配置的一部分)将允许 Go 工具更改路线并直接获取模块/版本 VCS 位置。
  • 例如,如果我们需要访问所有代理服务器,例如需要权限的位于gitlab 等地的代码,我们可以使用export GOPRIVATE=gitlab.XXX.com,gitlab.XXX-XX.com,XXX.io 多个域名用逗号分隔。

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:latest
INFO[7:11AM]: Exporter not specified. Traces won't be exported
2020-03-06 07:11:30.671249 I | Starting application at port :3000
  • 接着我们修改 GOPROXY 参数,指向本地3000端口,初始化我们之前的项目,再次执行go mod tidy
$ 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=200
INFO[7:39AM]: incoming request  http-method=GET http-path=/github.com/dreamerjackson/minidiv/@v/list http-status=200
INFO[7:39AM]: incoming request  http-method=GET http-path=/github.com/dreamerjackson/minidiv/@latest http-status=200
  • 详细信息,查看参考资料中 Athens 的官方网站

go module 优势

  • 提供脱离gopath管理 go 代码的优势
  • 提供了代码捆绑、版本控制、依赖管理的功能
  • 供全球开发人员使用、构建,下载,授权、验证,获取,缓存和重用模块(可以通过搭建自己的代理服务器来实现这些功能)
  • 可以验证模块(对于任何给定的版本)始终包含完全相同的代码,而不管它被构建了多少次,从何处获取以及由谁获取

总结

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

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

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

欢迎加微信:ywj2271840211

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
cloudy GoCN 每日新闻 (2020-03-27) 中提及了此贴 03月27日 18:35
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册