马上 2021 年了,Go 也即将在明年发布 Go1.16。但 Go Modules 仍然是大家关注的话题之一。
今天正式 分享出来,希望对大家的学习有所帮助。
Go 1.11 推出的模块(Modules)为 Go 语言开发者打开了一扇新的大门,理想化的依赖管理解决方案使得 Go 语言朝着计算机编程史上的第一个依赖乌托邦(Deptopia)迈进。
随着模块一起推出的还有模块代理协议(Module proxy protocol),通过这个协议我们可以实现 Go 模块代理(Go module proxy),也就是依赖镜像。
Go 1.13 的发布为模块带来了大量的改进,所以模块的扶正就是这次 Go 1.13 发布中开发者能直接感觉到的最大变化。
Go Modules 简介
快速迁移项目至 Go Modules
使用 Go Modules 时常遇见的坑
坑 1:判断项目是否启用了 Go Modules
坑 2:管理 Go 的环境变量
坑 3:从 dep、glide 等迁移至 Go Modules
坑 4:拉取私有模块
坑 5:更新现有的模块
坑 6:主版本号
Go Module Proxy 简介
Goproxy 中国(goproxy.cn)
Go modules (前身 vgo) 是 Go team (Russ Cox) 强推的一个理想化的类语言级依赖管理解决方案,它是和 Go1.11 一同发布的,在 Go1.13 做了大量的优化和调整,目前已经变得比较不错,如果你想用 Go modules,但还停留在 1.11/1.12 版本的话,强烈建议升级。
首先这并不是乱说的,因为 Go modules 确实是被强推出来的,如下:
从他强制要求使用语义化版本控制这一点来说就很理想化了,如下:
这个关键词其实是我自己瞎编的,我只是单纯地个人认为 Go modules 在设计上就像个语言级特性一样,比如如果你的主版本号发生变更,那么你的代码里的 import path 也得跟着变,它认为主版本号不同的两个模块版本是完全不同的两个模块。此外,Go moduels 在设计上跟 go 整个命令都结合得相当紧密,无处不在,所以我才说它是一个有点儿像语言级的特性,虽然不是太严谨。
那么在上文中提到的 Russ Cox 何许人也呢,很多人应该都知道他,他是 Go 这个项目目前代码提交量最多的人,甚至是第二名的两倍还要多。
Russ Cox 还是 Go 现在的掌舵人(大家应该知道之前 Go 的掌舵人是 Rob Pike,但是听说由于他本人不喜欢特朗普执政所以离开了美国,然后他岁数也挺大的了,所以也正在逐渐交权,不过现在还是在参与 Go 的发展)。
Russ Cox 的个人能力相当强,看问题的角度也很独特,这也就是为什么他刚一提出 Go modules 的概念就能引起那么大范围的响应。虽然是被强推的,但事实也证明当下的 Go modules 表现得确实很优秀,所以这表明一定程度上的 “独裁” 还是可以接受的,至少可以保证一个项目能更加专一地朝着一个方向发展。
总之,无论如何 Go modules 现在都成了 Go 语言的一个密不可分的组件。
Go modules 出现的目的之一就是为了解决 GOPATH 的问题,也就相当于是抛弃 GOPATH 了。
Go modules 还处于 Opt-in 阶段,就是你想用就用,不用就不用,不强制你。但是未来很有可能 Go2 就强制使用了。
有一点需要纠正,就是“模块”和“包”,也就是 “module” 和 “package” 这两个术语并不是等价的,是 “集合” 跟 “元素” 的关系,“模块” 包含 “包”,“包” 属于 “模块”,一个 “模块” 是零个、一个或多个 “包” 的集合。
module example.com/foobar
go 1.13
require (
example.com/apple v0.1.2
example.com/banana v1.2.3
example.com/banana/v2 v2.3.4
example.com/pineapple v0.0.0-20190924185754-1b0db40df49a
)
exclude example.com/banana v1.2.4
replace example.com/apple v0.1.2 => example.com/rda v0.1.0
replace example.com/banana => example.com/hugebanana
go.mod 是启用了 Go moduels 的项目所必须的最重要的文件,它描述了当前项目(也就是当前模块)的元信息,每一行都以一个动词开头,目前有以下 5 个动词:
这里的填写格式基本为包引用路径+版本号,另外比较特殊的是 go $version
,目前从 Go1.13 的代码里来看,还只是个标识作用,暂时未知未来是否有更大的作用。
go.sum 是类似于比如 dep 的 Gopkg.lock 的一类文件,它详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256 哈希值以备 Go 在今后的操作中保证项目所依赖的那些模块版本不会被篡改。
example.com/apple v0.1.2 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
example.com/apple v0.1.2/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= example.com/banana v1.2.3 h1:qHgHjyoNFV7jgucU8QZUuU4gcdhfs8QW1kw68OD2Lag=
example.com/banana v1.2.3/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= example.com/banana/v2 v2.3.4 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= example.com/banana/v2 v2.3.4/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
...
我们可以看到一个模块路径可能有如下两种:
example.com/apple v0.1.2 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
example.com/apple v0.1.2/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
前者为 Go modules 打包整个模块包文件 zip 后再进行 hash 值,而后者为针对 go.mod 的 hash 值。他们两者,要不就是同时存在,要不就是只存在 go.mod hash。
那什么情况下会不存在 zip hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的 zip hash,就会出现不存在 zip hash,只存在 go.mod hash 的情况。
这个环境变量主要是 Go modules 的开关,主要有以下参数:
这个环境变量主要是用于设置 Go 模块代理,主要如下:
https://proxy.golang.org,direct
,但很可惜 proxy.golang.org
在中国无法访问,故而建议使用 goproxy.cn
作为替代,可以执行语句:go env -w GOPROXY=https://goproxy.cn,direct
。刚刚在上面,我们可以发现值列表中有 “direct” ,它又有什么作用呢。其实值列表中的 “direct” 为特殊指示符,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等),当值列表中上一个 Go module proxy 返回 404 或 410 错误时,Go 自动尝试列表中的下一个,遇见 “direct” 时回源,遇见 EOF 时终止并抛出类似 “invalid version: unknown revision...” 的错误。
它的值是一个 Go checksum database,用于使 Go 在拉取模块版本时(无论是从源站拉取还是通过 Go module proxy 拉取)保证拉取到的模块版本数据未经篡改,也可以是“off”即禁止 Go 在后续操作中校验模块版本
<SUMDB_NAME>+<PUBLIC_KEY>
。<SUMDB_NAME>+<PUBLIC_KEY> <SUMDB_URL>
。sum.golang.org
(之所以没有按照上面的格式是因为 Go 对默认值做了特殊处理)。sum.golang.org
在中国无法访问,故而更加建议将 GOPROXY 设置为 goproxy.cn
,因为 goproxy.cn
支持代理 sum.golang.org
。Go checksum database 主要用于保护 Go 不会从任何源头拉到被篡改过的非法 Go 模块版本,其作用(左)和工作机制(右)如下图:
如果有兴趣的小伙伴可以看看 Proposal: Secure the Public Go Module Ecosystem,有详细介绍其算法机制,如果想简单一点,查看 go help module-auth
也是一个不错的选择。
这三个环境变量都是用在当前项目依赖了私有模块,也就是依赖了由 GOPROXY 指定的 Go module proxy 或由 GOSUMDB 指定 Go checksum database 无法访问到的模块时的场景
在使用上来讲,比如 GOPRIVATE=*.corp.example.com
表示所有模块路径以 corp.example.com
的下一级域名 (如 team1.corp.example.com
) 为前缀的模块版本都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 corp.example.com
本身。
这个主要是针对 Go modules 的全局缓存数据说明,如下:
$GOPATH/pkg/mod
和 $GOPATH/pkg/sum
下,未来或将移至 $GOCACHE/mod
和$GOCACHE/sum
下( 可能会在当 $GOPATH
被淘汰后)。go clean -modcache
清理所有已缓存的模块版本数据。另外在 Go1.11 之后 GOCACHE 已经不允许设置为 off 了,我想着这也是为了模块数据缓存移动位置做准备,因此大家应该尽快做好适配。
第一步: 升级到 Go 1.13。
第二步: 让 GOPATH 从你的脑海中完全消失,早一步踏入未来。
修改 GOBIN 路径(可选):go env -w GOBIN=$HOME/bin
。
打开 Go modules:go env -w GO111MODULE=on
。
设置 GOPROXY:go env -w GOPROXY=https://goproxy.cn,direct
# 在中国是必须的,因为它的默认值被墙了。
第三步(可选): 按照你喜欢的目录结构重新组织你的所有项目。
第四步: 在你项目的根目录下执行 go mod init <OPTIONAL_MODULE_PATH>
以生成 go.mod 文件。
第五步: 想办法说服你身边所有的人都去走一下前四步。
用 go help module-get
和 go help gopath-get
分别去了解 Go modules 启用和未启用两种状态下的 go get 的行为
用 go get
拉取新的依赖
拉取最新的版本(优先择取 tag):go get golang.org/x/text@latest
拉取 master
分支的最新 commit:go get golang.org/x/text@master
拉取 tag 为 v0.3.2 的 commit:go get golang.org/x/text@v0.3.2
拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:go get golang.org/x/text@342b2e
用 go get -u
更新现有的依赖
用 go mod download
下载 go.mod 文件中指明的所有依赖
用 go mod tidy
整理现有的依赖
用 go mod graph
查看现有的依赖结构
用 go mod init
生成 go.mod 文件 (Go 1.13 中唯一一个可以生成 go.mod 文件的子命令)
用 go mod edit
编辑 go.mod 文件
用 go mod vendor
导出现有的所有依赖 (事实上 Go modules 正在淡化 Vendor 的概念)
用 go mod verify
校验一个模块是否被篡改过
这里我们注意到有两点比较特别,分别是:
go mod vendor
,因为 Go modules 正在淡化 Vendor 的概念,很有可能 Go2 就去掉了。
这里主要是提到 Go1.13 新增了 go env -w
用于写入环境变量,而写入的地方是 os.UserConfigDir
所返回的路径,需要注意的是 go env -w
不会覆写。
这里主要是指从旧有的依赖包管理工具(dep/glide 等)进行迁移时,因为 BUG 的原因会导致不经过 GOPROXY 的代理,解决方法有如下两个:
这里主要想涉及两块知识点,如下:
在这里再次强调了 Go Module Proxy 的作用(图左),以及其对应的协议交互流程(图右),有兴趣的小伙伴可以认真看一下。
Q:如何解决 Go 1.13 在从 GitLab 拉取模块版本时遇到的,Go 错误地按照非期望值的路径寻找目标模块版本结果致使最终目标模块拉取失败的问题?
**A:**GitLab 中配合 goget 而设置的 <meta>
存在些许问题,导致 Go 1.13 错误地识别了模块的具体路径,这是个 Bug,据说在 GitLab 的新版本中已经被修复了,详细内容可以看 https://github.com/golang/go/issues/34094 这个 Issue。然后目前的解决办法的话除了升级 GitLab 的版本外,还可以参考 https://github.com/developer-learning/night-reading-go/issues/468#issuecomment-535850154 这条回复。
Q:使用 Go modules 时可以同时依赖同一个模块的不同的两个或者多个小版本(修订版本号不同)吗?
**A:**不可以的,Go modules 只可以同时依赖一个模块的不同的两个或者多个大版本(主版本号不同)。比如可以同时依赖 example.com/foobar@v1.2.3 和 example.com/foobar/v2@v2.3.4,因为他们的模块路径(module path)不同,Go modules 规定主版本号不是 v0 或者 v1 时,那么主版本号必须显式地出现在模块路径的尾部。但是,同时依赖两个或者多个小版本是不支持的。比如如果模块 A 同时直接依赖了模块 B 和模块 C,且模块 A 直接依赖的是模块 C 的 v1.0.0 版本,然后模块 B 直接依赖的是模块 C 的 v1.0.1 版本,那么最终 Go modules 会为模块 A 选用模块 C 的 v1.0.1 版本而不是模块 A 的 go.mod 文件中指明的 v1.0.0 版本。
这是因为 Go modules 认为只要主版本号不变,那么剩下的都可以直接升级采用最新的。但是如果采用了最新的结果导致项目 Break 掉了,那么 Go modules 就会 Fallback 到上一个老的版本,比如在前面的例子中就会 Fallback 到 v1.0.0 版本。
Q:在 go.sum 文件中的一个模块版本的 Hash 校验数据什么情况下会成对出现,什么情况下只会存在一行?
**A:**通常情况下,在 go.sum 文件中的一个模块版本的 Hash 校验数据会有两行,前一行是该模块的 ZIP 文件的 Hash 校验数据,后一行是该模块的 go.mod 文件的 Hash 校验数据。但是也有些情况下只会出现一行该模块的 go.mod 文件的 Hash 校验数据,而不包含该模块的 ZIP 文件本身的 Hash 校验数据,这个情况发生在 Go modules 判定为你当前这个项目完全用不到该模块,根本也不会下载该模块的 ZIP 文件,所以就没必要对其作出 Hash 校验保证,只需要对该模块的 go.mod 文件作出 Hash 校验保证即可,因为 go.mod 文件是用得着的,在深入挖取项目依赖的时候要用。
Q:能不能更详细地讲解一下 go.mod 文件中的 replace 动词的行为以及用法?
**A:**这个 replace 动词的作用是把一个“模块版本”替换为另外一个“模块版本”,这是“模块版本”和“模块版本(module path)”之间的替换,“=>”标识符前面的内容是待替换的“模块版本”的“模块路径”,后面的内容是要替换的目标“模块版本”的所在地,即路径,这个路径可以是一个本地磁盘的相对路径,也可以是一个本地磁盘的绝对路径,还可以是一个网络路径,但是这个目标路径并不会在今后你的项目代码中作为你“导入路径(import path)”出现,代码里的“导入路径”还是得以你替换成的这个目标“模块版本”的“模块路径”作为前缀。
另外需要注意,Go modules 是不支持在 “导入路径” 里写相对路径的。举个例子,如果项目 A 依赖了模块 B,比如模块 B 的“模块路径”是 example.com/b,然后它在的磁盘路径是 ~/b,在项目 A 里的 go.mod 文件中你有一行 replace example.com/b=>~/b,然后在项目 A 里的代码中的“导入路基”就是 import"example.com/b",而不是 import"~/b",剩下的工作是 Go modules 帮你自动完成了的。
然后就是我在分享中也提到了, exclude 和 replace 这两个动词只作用于当前主模块,也就是当前项目,它所依赖的那些其他模块版本中如果出现了你待替换的那个模块版本的话,Go modules 还是会为你依赖的那个模块版本去拉取你的这个待替换的模块版本。
举个例子,比如项目 A 直接依赖了模块 B 和模块 C,然后模块 B 也直接依赖了模块 C,那么你在项目 A 中的 go.mod 文件里的 replace c=>~/some/path/c 是只会影响项目 A 里写的代码中,而模块 B 所用到的还是你 replace 之前的那个 c,并不是你替换成的 ~/some/path/c 这个。
在 Go1.13 发布后,接触 Go modules 和 Go module proxy 的人越来越多,经常在各种群看到各种小伙伴在咨询,包括我自己也贡献了好几枚 “坑”。
如果后续大家还有什么建议或问题,欢迎随时来讨论。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/uUNTH06_s6yzy5urtjPMsg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为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 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。