Go语言自从推出了作为版本管理工具后,结束Go语言版本管理工具的纷争,实现了大一统。
相信有很多人都对这个版本管理的机制都有基础的概念、但并不深入。而官方把最核心的实现,都放在这一篇 https://golang.org/ref/mod 文档中。
今天,我们一起来读读这一篇文章。
新手直接阅读这篇文章的门槛有点高,我建议可以先看看下面这五篇较为通俗的官方博客,能帮助我们了解一些背景知识。
https://blog.golang.org/using-go-modules
go.mod放在项目的根目录,抛弃原来的GOPATH
用MAJOR.MINOR.PATCH格式管理版本,详细可参考Semantic Versioning 2.0.0
用go.sum保证依赖文件被完整下载(如果公司搭建私有库就会出现校验问题,需要关闭GOSUM)
项目内部的库,不再是相对路径,而用的是 go mod模块名 + 相对路径 ,定义更加清晰
第四点的价值很大,可读性大大提高,我们可以将一整个path来辅助命名,如 market/order,前者market可以帮助后者order的含义做一定补充
https://blog.golang.org/migrating-to-go-modules
将项目迁移到go mod,主要讲的是对接老的版本管理系统,如。
https://blog.golang.org/publishing-go-modules
版本命名规则:推荐不稳定版本用开始,稳定后改成。
pseudo-version 直译为假的版本,一般是直接依赖branch,而不是按规范依赖tag
https://blog.golang.org/v2-go-modules
注意主版本名为之后的,项目里对应的要新增一个或者更高版本号的目录。
我个人感觉升级到v2这种方式不太友好,需要我们再维护一整个目录
https://blog.golang.org/module-compatibility
用不定参数...的特性提高函数的入参扩展性
引入函数式的参数,也就是将具体的执行逻辑作为参数,传递进来
引入Option types模式,更详细地可以参考我的文章
用接口interface分离调用和实现
如果结构体struct明确不能对比,就用一个doNotCompare的field来告诉调用者
type doNotCompare [0]func()
type Point struct {
doNotCompare
X int
Y int
} 第五点非常有意思,也就是要求我们在前期设计对象时,就要考虑清楚这个对象后续的特性,比如示例中的是否可以对比
进入正题,我们来一起看看,它的定义简单示例如下:
module example.com/my/thing
go 1.12
require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]
IDE会帮助你格式化,记住以下关键词即可(重点为前三个)
module - go mod init指令定义的库名
go - 要求go语言的最低版本,会影响到后面依赖库的下载
require - 必备库,也就是代码中直接import的部分
replace - 替换库,在重构时挺好用(比如某个开源组件有问题,内部fork了一版,直接replace即可)
retract 撤回版本,告诉调用本库的项目,部分版本有严重问题、不要引用
go mod 底层实现依赖 - MVS 最小版本选择。 这个特性很有意思,后续单独来讲讲这块,一开始就不深入到细节了
在我们整理文件时,经常能看到两个奇怪的字符和。我们来详细地分析一下。
上面我们已经讲过,如果一个库的tag为v1以上,如v2,就必须得创建一个v2的目录。例如
这就要求我们在项目下新建v2目录,再存放代码。但是,很多库往往只是升级个主版本号,并不会去新建目录、还需要迁移代码。为了兼容这个情况,就会引入。例如
我们先聊一个简单的场景:当前项目为A,调用了项目B,B又调用了C。对A进行编译,需要B和C的相关代码。
在完全规范的项目中:
条件1 - A的go.mod里包含B
条件2 - B的go.mod里包含C
在编译A时,会在go.mod找到B的信息,所以B是字段;而C的信息已经被维护在B的go.mod里了,不需要在A的go.mod里维护。
而什么样的情况会发生indirect呢?它对应的是 条件2 缺失的场景
B没有启用Go Module,采用的是老项目管理方式
B的go.mod部分缺失,未填写模块C
最常见的部分缺失场景是:项目虽然有go.mod,但实际编译不走Go Module,而是如vendor目录等方式
用一句话总结,A库无法根据B库的go.mod找到C库。
相关的指令有很多,我重点分两块来说:
先是高频使用的命令:
初始化项目,保证module名称与git路径一致。
例如
常见的flags
-d 只更新go.mod中的依赖,轻量级
-u 更新指定库与依赖它的库,全量
例如
其中download是下载到Go Module的缓存中,而vendor是下载到vendor依赖路径。官方推荐前者。
我经常会去手动编辑文件,然后用这个指令刷新一下依赖库
整理并更新go mod的依赖信息,保证当前的为最新。
然后是排查依赖库问题用到的:
go list -m all 查看本项目的所有依赖库与版本
go list -m -versions {module名} 查看module支持的版本号
go list -m -json {module名}@{版本号} 用json格式查看指定module版本号的信息,如创建时间
查看所有go mod的依赖,一般在查依赖关系时用到
查指定库是怎么被依赖的
查看指定(go文件编译的)二进制文件的版本信息
大部分人使用的最大问题是无法下载代码库,也就是代理的设置,网上也有很多教程,我这边给三个我常用的:
阿里云:GOPROXY=https://mirrors.aliyun.com/goproxy,direct
七牛云:GOPROXY=https://goproxy.cn,direct
全球代理:GOPROXY=https://goproxy.io,direct
公司私有库需要私有代理。
本讲的内容到这里就告一段落了,相信通过这篇文章,大家已经能应对绝大部分Go Module的场景。
下一讲,我会重点讲Go Module最核心的 Minimal version selection (MVS) 机制。
Github: https://github.com/Junedayday/code_reading Blog: http://junes.tech/ Bilibili: 网页链接 公众号: golangcoding