哈喽,大家好, 一般编程语言都会提供依赖库管理工具,例如python的pip、node.js的npm,java的maven,rust的cargo,Go语言也有提供自己的依赖库管理工具,Go语言在1.11提出了Go mod,每次版本或多或少都会对go.mod进行改进优化,go mod也越来越好,当前大多数公司都使用go mod来管理依赖库,所以本文我们一起来入门go mod(参考资料文末给出);
起初Go语言在1.5之前没有依赖管理工具,若想引入依赖库,需要执行go get命令将代码拉取放入GOPATH/src目录下,作为GOPATH下的全局依赖,这也就意味着没有版本控制及隔离项目的包依赖;
为了解决隔离项目的包依赖问题,Go1.5版本推出了vendor机制,环境变量中有一个GO15VENDOREXPERIMENT需要设置为1,该环境变量在Go1.6版本时变成默认开启,目前已经退出了历史舞台;
vendor其实就是将原来放在GOPATH/src的依赖包放到工程的vendor目录中进行管理,不同工程独立地管理自己的依赖包,相互之间互不影响,原来是包共享的模式,通过vendor这种机制进行隔离,在项目编译的时候会先去vendor目录查找依赖,如果没有找到才会再去GOPATH目录下查找;
优点:保证了功能项目的完整性,减少了下载依赖包,直接使用vendor就可以编译
缺点:仍然没有解决版本控制问题,go get仍然是拉取最新版本代码;
很多优秀的开发者在这期间也都实现了不错的包依赖管理工具,例如:
godep:https://github.com/tools/godep
govendor:https://github.com/kardianos/govendor
glide:https://github.com/Masterminds/glide
dep:https://github.com/golang/dep
dep应该是其中最成功的,得到了Go语言官方的支持,该项目也被放到了https://github.com/golang/dep,但是为什么dep没有称为官宣的依赖工具呢?
其实因为随着Russ Cox 与 Go 团队中的其他成员不断深入地讨论,发现 dep 的一些细节似乎越来越不适合 Go,因此官方采取了另起 proposal 的方式来推进,其方案的结果一开始先是释出 vgo,最终演变为我们现在所见到的 Go modules;
go modules是Russ Cox推出来的,发布于Go1.11,成长于Go1.12,丰富于Go1.13,正式于Go1.14推荐在生产上使用,几乎后续的每个版本都或多或少的有一些优化,在Go1.16引入go mod retract、在Go1.18引入go work工作区的概念,这些我们在本文都会介绍到;
这个环境变量是Go Modules的开关,主要有以下参数:
我现在使用的Go版本是1.19.3,默认GO111MODULE=on,感觉该变量也会像GO15VENDOREXPERIMENT最终推出系统环境变量的舞台;
该环境变量用于设置Go模块代理,Go后续在拉取模块版本时能够脱离传统的VCS方式从镜像站点快速拉取,GOPROXY的值要以英文逗号分割,默认值是https://proxy.golang.org,direct
,但是该地址在国内无法访问,所以可以使用goproxy.cn
来代替(七牛云配置),设置命令:
go env -w GOPROXY=GOPROXY=https://goproxy.cn,direct
也可以使用其他配置,例如阿里配置:
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/
该环境变量也可以关闭,可以设置为"off",禁止Go在后续操作中使用任何Go module proxy;
上面的配置中我们用逗号分割后面的值是direct
,它是什么意思呢?
direct
为特殊指示符,因为我们指定了镜像地址,默认是从镜像站点拉取,但是有些库可能不存在镜像站点中,direct
可以指示Go回源到模块版本的源地址去抓取,比如github,当go module proxy返回404、410这类错误时,其会自动尝试列表中的下一个,遇见direct
时回源地址抓取;
该环境变量的值是一个Go checksum database,用于保证Go在拉取模块版本时拉取到的模块版本数据未经篡改,若发现不一致会中止,也可以将值设置为off
即可以禁止Go在后续操作中校验模块版本;
什么是Go checksum database?
Go checksum database主要用于保护Go不会从任何拉到被篡改过的非法Go模块版本,详细算法机制可以看一下:https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md#proxying-a-checksum-database
GOSUMDB的默认值是sum.golang.org
,默认值与自定义值的格式不一样,默认值在国内是无法访问,这个值我们一般不用动,因为我们一般已经设置好了GOPROXY,goproxy.cn支持代理sum.golang.org
;
GOSUMDB的值自定义格式如下:
<SUMDB_NAME>+<PUBLIC_KEY>
。<SUMDB_NAME>+<PUBLIC_KEY> <SUMDB_URL>
。这三个环境变量放在一起说,一般在项目中不经常使用,这三个环境变量主要用于私有模块的拉取,在GOPROXY、GOSUMDB中无法访问到模块的场景中,例如拉取git上的私有仓库;
GONOPROXY、GONOSUMDB的默认值是GOPRIVATE的值,所以我们一般直接使用GOPRIVATE即可,其值也是可以设置多个,以英文逗号进行分割;例如:
$ go env -w GOPRIVATE="github.com/asong2020/go-localcache,git.xxxx.com"
也可以使用通配符的方式进行设置,对域名设置通配符号,这样子域名就都不经过Go module proxy和Go checksum database;
go mod download会将依赖缓存到本地,缓存的目录是GOPATH/pkg/mod/cache
、GOPATH/pkg/sum
,这些缓存依赖可以被多个项目使用,未来可能会迁移到$GOCACHE
下面;
可以使用go clean -modcache
清理所有已缓存的模块版本数据;
我们可以使用go help mod
查看可以使用的命令:
$ go env -w GOPRIVATE="github.com/asong2020/go-localcache,git.xxxx.com"
命令 | 作用 |
---|---|
go mod init | 生成go.mod文件 |
go mod download | 下载go.mod文件中指明的所有依赖放到全局缓存 |
go mod tidy | 整理现有的依赖,添加缺失或移除不使用的modules |
go mod graph | 查看现有的依赖结构 |
go mod edit | 编辑go.mod文件 |
go mod vendor | 导出项目所有的依赖到vendor目录 |
go mod verify | 校验一个模块是否被篡改过 |
go mod why | 解释为什么需要依赖某个模块 |
go.mod是启用Go modules的项目所必须且最重要的文件,其描述了当前项目的元信息,每个go.mod文件开头符合包含如下信息:
module:用于定义当前项目的模块路径(突破$GOPATH路径)
go:当前项目Go版本,目前只是标识作用
require:用设置一个特定的模块版本
exclude:用于从使用中排除一个特定的模块版本
replace:用于将一个模块版本替换为另外一个模块版本,例如chromedp使用golang.org/x/image这个package一般直连是获取不了的,但是它有一个github.com/golang/image的镜像,所以我们要用replace来用镜像替换它
restract:用来声明该第三方模块的某些发行版本不能被其他模块使用,在Go1.16引入
例子:
image-20230102181623314
接下来我们分模块详细介绍一下各部分;
go.mod文件的第一行是module path,采用仓库+module name的方式定义,例如上面的项目:
module github.com/asong2020/go-localcache
因为Go module遵循语义化版本规范2.0.0,所以如果工程的版本已经大于2.0.0,按照规范需要加上major的后缀,module path改成如下:
module github.com/asong2020/go-localcache/v2
module github.com/asong2020/go-localcache/v3
......
go.mod文件的第二行是go version,其是用来指定你的代码所需要的最低版本:
go 1.19.3
其实这一行不是必须的,目前也只是标识作用,可以不写;
require用来指定该项目所需要的各个依赖库以及他们的版本,从上面的例子中我们看到版本部分有不同的写法,还有注释,接下来我们来解释一下这部分;
github.com/davecgh/go-spew v1.1.0 // indirect
以下场景才会添加indirect注释:
Go1.17版本对此做了优化,indirect 的 module 将被放在单独 require 块的,这样看起来更加清晰明了。
我们在项目中会看到有一些库后面添加了incompatible标记:
github.com/dgrijalva/jwt-go v3.2.0+incompatible
jwt-go这个库就是这样的,这是因为jwt-go的版本已经大于2了,但是他们的module path仍然没有添加v2、v3这样的后缀,不符合Go的module管理规范,所以go module把他们标记为incompatible,不影响引用;
go module拉取依赖包本质也是go get行为,go get主要提供了以下命令:
命令 | 作用 |
---|---|
go get | 拉取依赖,会进行指定性拉取(更新),并不会更新所依赖的其它模块。 |
go get -u | 更新现有的依赖,会强制更新它所依赖的其它全部模块,不包括自身。 |
go get -u -t ./... | 更新所有直接依赖和间接依赖的模块版本,包括单元测试中用到的。 |
go get拉取依赖包取决于依赖包是否有发布的tags:
2 . 拉取的依赖包有发布tags
没有发布的tags:
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
v0.0.0:根据commit的base version生成的:
20190718012654:是这次提交的时间,格式是yyyyMMddhhmmss
fb15b899a751:是这个版本的commit id,通过这个可以确定这个库的特定的版本
github.com/beego/bee v1.12.0
replace用于解决一些错误的依赖库的引用或者调试依赖库;
场景举例:
举例1:
日常开发离不开第三方库,大部分场景都可以满足我们的需要,但是有些时候我们需要对依赖库做一些定制修改,依赖库修改后,我们想引起最小的改动,就可以使用replace命令进行重新引用,调试也可以使用replace进行替换,Go1.18引入了工作区的概念,调试可以使用work进行代替,后面会介绍;
举例2:
golang.org/x/crypto库一般我们下载不下来,可以使用replace引用到github.com/golang/crypto:
go mod edit -replace golang.org/x/crypto=github.com/golang/crypto@v0.0.0-20160511215533-1f3b11f56072
用于跳过某个依赖库的版本,使用场景一般是我们知道某个版本有bug或者不兼容,为了安全起可以使用exclude跳过此版本;
exclude (
go.etcd.io/etcd/client/v2 v2.305.0-rc.0
)
这个特性是在Go1.16版本中引入,用来声明该第三方模块的某些发行版本不能被其他模块使用;
使用场景:发生严重问题或者无意发布某些版本后,模块的维护者可以撤回该版本,支持撤回单个或多个版本;
这种场景以前的解决办法:
维护者删除有问题版本的tag,重新打一个新版本的tag;
使用者发现有问题的版本tag丢失,手动介入升级,并且不明真因;
引入retract后,维护者可以使用retract在go.mod中添加有问题的版本:
// 严重bug...
retract (
v0.1.0
v0.2.0
)
重新发布新版本后,在引用该依赖库的使用执行go list可以看到 版本和"严重bug..."的提醒;
该特性的主要目的是将问题更直观的反馈到开发者的手中;
go.sun文件也是在go mod init阶段创建,go.sum的介绍文档偏少,我们一般也很少关注go.sum文件,go.sum主要是记录了所有依赖的module的校验信息,内容如下:
image-20230102193717816
从上面我们可以看到主要是有两种形式:
其中module是依赖的路径,version是依赖的版本号。hash是以h1:
开头的字符串,hash 是 Go modules 将目标模块版本的 zip 文件开包后,针对所有包内文件依次进行 hash,然后再把它们的 hash 结果按照固定格式和算法组成总的 hash 值。
h1 hash 和 go.mod hash两者要不同时存在,要不就是只存在go.mod hash,当Go认为肯定用不到某个版本的时候就会省略它的h1 hash,就只有go.mod hash;
使用go modules的一个前置条件是Go语言版本大于等于Go1.11;
然后我们要检查环境变量GO111MODULE是否开启,执行go env
查看:
$ go env
GO111MODULE=off
执行如下命令打开go mod:
$ go env -w GO111MODULE=on
接下来我们随意创建一个项目:
$ mkdir -p asong/demo
$ cd asong/demo
执行go mod init初始化该项目:
$ go mod init github.com/asong/demo
go: creating new go.mod: module github.com/asong/demo
接下来我们在demo目录下创建main.go文件,写下如下代码:
package main
import (
"fmt"
cache "github.com/asong2020/go-localcache"
)
func main() {
c, err := cache.NewCache()
if err != nil {
return
}
key := "asong"
value := []byte("公众号:Golang梦工厂")
err = c.Set(key, value)
if err != nil {
return
}
entry, err := c.Get(key)
if err != nil {
return
}
fmt.Printf("get value is %s\n", string(entry))
err = c.Delete(key)
if err != nil {
return
}
}
然后执行go mod tidy命令:
image-20230102202521167
自动根据main.go文件更新依赖,我们再看一下go.mod文件:
image-20230102202600643
以上就是在项目对go.mod的简单使用;
工作区用来解决什么问题?
场景1:我们有时在本地会对一些三方依赖库进行特制修改,然后想在项目修改依赖库引用到本地进行调试,这时我们可以使用replace做替换,这样就可以在本地进行开发联调,这样虽然可以解决问题,但是会存在问题,因为是在项目的go.mod文件直接修改的,如果误传到远程仓库,会影响到其他开发同学;
场景2:我们在本地开发了一些依赖库,这时想在本地测试一下,还未发到远程仓库,那么我们在其他项目中引入该依赖库后,执行go mod tidy
就会报远程库没有找到的问题,所以就必须要把依赖库先推送到远程,在引用调试;
正是这些问题,Go语言在Go1.18正式增加了go work
工作区的概念,其实就是将N个Go Module组成一个Go Work,工作区的读取优先级是最高的,执行go help work
可以查看go work提供的功能:
$ go help work
Usage:
go work <command> [arguments]
The commands are:
edit edit go.work from tools or scripts
init initialize workspace file
sync sync workspace build list to modules
use add modules to workspace file
Use "go help work <command>" for more information about a command.
执行go work init
命令初始化一个新的工作区,在项目中生成一个go.work文件:
go 1.18
// 多模块添加
use (...)
replace XXXXX => XXXX v1.4.5
go.work文件与go.mod文件语法一致,go.work支持三个指令:
所以针对上述场景,我们使用go work init命令在项目中对本地依赖库进行关联即可解决,后续我们只需要在git配置文件中添加go.work文件不推送到远程即可;
我们也可以在编译时通过-workfile=off
指令禁用工作区模式:
$ go build -workfile=offf .
go.work的推出主要是用于在本地调试,不会因为修改go.mod引入问题;
现在大小公司的项目应该都已经在使用Go Modules
进行依赖包管理了,虽然Go Modules
相比于Maven、npm还不是很完善,但也在不断地进行优化,变得越来越好,如果你现在项目还没有使用go modules
,可以准备将项目迁移到go mod了,推荐你使用;
好啦,本文到这里就结束了,我们下期见。
本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/FEPXDMFfvKgmtsymVTWzHw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。