Go Modules是Go 言语从 1.11 版别之后官方推出的版别办理东西。Modules官方界说为:

模块是相关Go包的集合。modules是源代码交流和版别控制的单元。 go指令直接支撑运用modules,包括记载和解析对其他模块的依靠性。modules替换旧的基于GOPATH的方法来指定在给定构建中运用哪些源文件。

运用modules依靠办理的优点是非常显着的:

  • 主动下载依靠包
  • 不需求将代码再放入$GOPATH/src
  • 一切来自第三方的包都会指定版别(运用dep是无法指定第三方包的版别的)
  • 关于现已搬运的包,能够用replace在go.mod文件中替换,不需求修正代码

如何运用Modules

  1. 把 golang 升级到 1.11+ (现在现已到1.19了)
  2. 设置GO111MODULE

GO111MODULE

GO111MODULE 有三个值:off, onauto(默认值)

  • GO111MODULE=off,go指令即将不会支撑module功用,寻觅依靠包的方法将会沿用旧版别那种经过vendor目录或许GOPATH模式来查找。
  • GO111MODULE=on,go指令行会运用modules,而一点也不会去GOPATH目录下查找。
  • GO111MODULE=auto,默认值,go指令即将会依据当时目录来决定是否启用module功用。这种状况下能够分为两种情形:
    • 当时目录在$GOPATH/src之外且该目录包括go.mod文件
    • 当时文件在包括go.mod文件的目录下面。

当modules 功用启用时,依靠包的存放位置变更为$GOPATH/pkg,允许同一个package多个版别并存,且多个项目能够同享缓存的 module。

注:运用go modules最好搭配go proxy一起运用,否则在go get一些包时会呈现get不到的问题

go.mod

go mod指令

指令 阐明
download download modules to local cache(下载依靠包到本地缓存)
edit edit go.mod from tools or scripts (编辑go.mod)
graph print module requirement graph (打印模块依靠图)
init initialize new module in current directory (在当时目录初始化go.mod)
tidy add missing and remove unused modules(拉取短少的模块,移除不必的模块)
vendor make vendored copy of dependencies(将依靠复制到vendor下)
verify verify dependencies have expected content (验证依靠是否正确)
why explain why packages or modules are needed(解释为什么需求依靠)

通常在项目中咱们经过go get或许是go install的方法拉取指定版别的依靠包: 履行 go get指令,在下载依靠包的一起还能够指定依靠包的版别

  • 运转go get -u指令会将项目中的包升级到最新的非有必要版别或许修订版别
  • 运转go get -u=patch指令会将项目中的包升级到最新的修订版别
  • 运转go get [包名]@[版别号]指令会下载对应包的指定版别或许将对应包升级到指定的版别

当然咱们也能够先不go get,在最后go run main.go时,go.mod同样会主动查找依靠主动下载

go.mod关键字

指令字 作用
module 指定包的姓名(途径)
require 指定依靠项模块
replace 替换依靠项模块
exclude 忽略依靠项模块

进入go.mod文件中,根本都是这样的结构

[Golang早读] 谈谈 go.mod、go.sum、go.work
注:indirect 表明这个库是直接引用进来的

go.sum

相较于go.mod,go.sum看上去就像是天书,可读性实在是太低,可是咱们日常开发仍不得不要跟其打交道(通常是处理这个文件带来的兼并抵触,或是想手动调整其间的内容)。

先下一个粗浅界说:go.sum便是依据hash来检测下载下来的依靠,是不是和该版别依靠复合

go.sum格局

go.sum的内容格局根本上是如图所示

[Golang早读] 谈谈 go.mod、go.sum、go.work

go.sum的每一行都是一个条目,大致是这样的格局

<module> <version>/go.mod <hash>

或许

<module> <version> <hash>
<module> <version>/go.mod <hash>

其间module是依靠的途径, version是依靠的版别号,hash是以h1最初的字符串,表明生成checksum的算法是第一版的hash算法(sha256)

需求特殊阐明一下,version的确认规矩,比较复杂:

一、项目是否打tag?

如果项目没有打 tag,会生成一个版别号,格局如下:
v0.0.0-commit日期-commitID

比方github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

引用一个项目的特定分支,比方 develop branch,也会生成相似的版别号:
v当时版别+1-commit日期-commitID

比方github.com/DATA-DOG/go-sqlmock v1.3.4-0.20191205000432-012d92843b00 h1:Cnt/xQ9MO4BiAjZrVpl0BiqqtTJjXUkWhIqwuOCVtWo=

二、项目有没有用 go module?

如果项目有用到 go module,那么便是正常地用 tag 来作为版别号。

比方github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=

如果项目打了 tag,可是没有用到 go module,为了跟用了 go module 的项目相差异,需求加个+incompatible的标志。

比方github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=

三、项目用的 go module 版别是不是 v2+?

关于 go module v2+ 的特性,能够参阅 Go 的官方文档:blog.golang.org/v2-go…。简略而言,便是经过让依靠途径带版别号后缀来区分同一个项目里不同版别的依靠,相似于gopkg.in/xxx.v2的效果。

关于运用了 v2+ go module 的项目,项目途径会有个版别号的后缀。

比方github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=

Go在依靠办理时引进go.sum,是为了完成下面的目标

1. 提供分布式环境下的包办理依靠内容校验

Go爱用分布式的方法来办理包,这意味着缺乏一个可供信赖的中心来校验每个包的一致性。发布者在 GitHub 上给自己的项目打上 0.1 的 tag 之后,仍旧能够删掉这个 tag ,提交不同的内容后再从头打个 0.1 的 tag。哪怕发布者都是老实人,发布渠道也可能作恶。所以只能在每个项目里存储自己依靠到的一切组件的 checksum,才干保证每个依靠不会被篡改。

2. 作为 transparent log 来加强安全性

go.sum 还有一个很特别的地方,便是它不只是记载了当时依靠的checksum,还保留了历史上每次依靠的 checksum。这种做法效法了 transparent log 的概念。transparent log 旨在维护一个 Append Only 的日志记载,提高篡改者的作案成本,一起方便审查哪些记载是篡改进来的。依据Proposal: Secure the Public Go Module Ecosystem的说法,go.sum 之所以要用 transparent log 的形式记载历史上的每个checksum,是为了便于 sum db 的作业。

go.sum的引进带来的麻烦

1. 容易发生兼并抵触

这恐怕是 go.sum 最为人诟病的地方了。由于许多项目都没有经过打tag的方法来办理发布,每个commit都相当于新发布一个版别,这导致拉取它们的代码时会偶然往 go.sum 文件里插入一条新记载。go.sum会记载直接依靠的特性,更是让这种状况雪上加霜。

(go.sum这一块的内容非原创,来自谈谈go.sum – Go言语中文网 – Golang中文社区 (studygolang.com))

go.work

随着go 1.18版别的正式发布,多模块作业区也被引进(WorkSpaces),多模块作业区能够使开发者能够更容易地一起处理多个模块的作业, 如:方便进行依靠的代码调试(打断点、修正代码)、排查依靠代码 bug 。方便一起进行多个库房/模块并行开发调试

go work支撑指令

  • go work init 初始化作业区文件,用于生成go.work作业区文件

初始化并写入一个新的go.work到当时途径下,能够指定需求增加的代码模块

eg: go work init ./hello 将本地库房hello增加到作业区

hello库房有必要是gomod依靠办理的库房

  • go work use增加新的模块到作业区

eg:

go work use ./example 增加一个模块到作业区

go work use ./example ./example1 增加多个模块到作业区

go work use -r ./example 递归./example目录到当时作业区

删去指令: go work edit -dropuse=./example功用

  • go work sync将作业区的构建列表同步到作业区模块
  • go env GOWORK检查环境变量,检查当时作业区文件途径,能够排查作业区文件是否设置正确,go.work途径找不到能够运用GOWORK指定

go.work文件结构

go.work文件结构和go.mod文件结构相似,支撑Go版别号、指定作业区和需求替代的库房

[Golang早读] 谈谈 go.mod、go.sum、go.work

use指定运用的模块目录,能够运用go work use增加模块,也能够手动修正go.work作业区增加新的模块,在作业区中增加了模块途径,编译时会主动运用use中的本地代码进行编译

replaces替换依靠库房地址replaces指令与go.mod指令相同,用于替换项目中依靠的库房地址,需求注意的是,replacesuse不能一起指定相同的本地代码途径

go.work文件优先级高于go.mod

在一起运用go.work和go.mod的replaces功用时,别离指定不同的代码库房途径,go.work优先级高于go.mod中界说

//go.mod中界说替换为本地库房example
replace ( 
    github.com/link1st/example => ./example 
)
//go.work中界说替换为本地库房example1
replace ( 
    github.com/link1st/example => ./example1 
)

在代码构建时,运用的是go.work指定的example1库房代码,go.work优先级别更高

一些注意点

  • 通常状况下,建议不要提交go.work到git上,由于他主要用于本地代码开发
  • 运用go.work时,不同的项目文件有必要在有同一个根目录, 推荐在$GOPATH途径下履行,生成go.work文件
  • 现在仅go build会对go.work做出判别,go mod tidy不会影响作业区