06|构建模式:Go是怎么解决包依赖管理问题的?

2021-10-25 Tony Bai
《Tony Bai · Go 语言第一课》
课程介绍


讲述:Tony Bai

时长:大小25.11M


你好,我是 Tony Bai。
通过前面的讲解,我们已经初步了解了 Go 程序的结构,以及 Go 项目的典型布局了。那么,接下来,我们是时候来系统学习一下 Go 应用的构建了,它们都是我们继续 Go 语言学习的前提。
所以在这一节课,我们就来了解 Go 构建模式演化的前世今生。理解了这个发展史后,我们会重点来探讨现在被广泛采用的构建模式,Go Module 的基本概念和应用构建方式。  接着,知道了怎么做后,我们会再深一层,继续分析 Go Module 的工作原理。这样层层深入地分析完后,你就能彻底、透彻地掌握 Go Module 构建模式了。
好了,我们直接开始吧。我们先来了解一下 Go 构建模式的演化过程,弄清楚 Go 核心开发团队为什么要引入 Go module 构建模式。

Go 构建模式是怎么演化的?

Go 程序由 Go 包组合而成的,Go 程序的构建过程就是确定包版本、编译包以及将编译后得到的目标文件链接在一起的过程。
Go 语言的构建模式历经了三个迭代和演化过程,分别是最...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • 张诚
    2021-10-25
    目前网上讲包管理讲得最清晰最全面的内容了。

    作者回复: 过奖了:)

    共 6 条评论
    49
  • 罗杰
    2021-10-25
    从构建来看,现在的确是掌握 Go 的好时机。之前入坑的时候,gopath 真的让人抓狂。
    
    27
  • 本
    2021-10-25
    if go version < 1.13: 项目移出GOPATH/src go env -w GO111MODULE=on go mod init module_path go mod tidy
    共 2 条评论
    12
  • lesserror
    2021-10-25
    感谢 Tony Bai 老师的分享,每篇文章我都认真拜读了。 本着持续精进的目的,每篇文章我都会提出一些自己的疑问,麻烦老师抽空解答一下。 1. 在$GOPATH模式下,寻找第三方包依赖的顺序是不是:先找 $GOROOT 然后找 $GOPATH。 2. go.sum文件的工作原理后面能否详细讲讲吗? 感觉这里的讲解还是简单带过了。 3. 文中说:“Go 团队认为“最小版本选择”为 Go 程序实现持久的和可重现的构建提供了最佳的方案。” 这句话能展开讲讲吗? 感觉没明白其中的意思。 4. 第4讲说到:“Go 支持在一个项目 / 仓库中存在多个 module,但这种管理方式可能要比一定比例的代码重复引入更多的复杂性。” 如果项目中引入了第三方 module,比如这篇文章中也引入了第三方 module,这种情况属于 : "在一个项目 / 仓库中存在多个 module" 的情况吗?
    展开

    作者回复: 你的提问就是对专栏最好的支持,手工点赞! 下面回答你的问题: 1. 从gopath模式下,go build命令的输出来看,是这样的。 2. go.sum机制对于大多数开发人员都是透明的,属于高级话题。因此,在我的这个以入门和基础为重的专栏中并未深入展开。后>续我可能会在自己的博客或公号上谈谈go.sum机制,到时候,你可以去看看。 3. 相较于选择最新最大版本,选择最小版本出于几个考虑吧: 1)对开发者而言,更易于理解和预测,就像课程中例子那样,我们根据依赖图可以很容易确定程序构建最终使用的依赖版本。 2) 对go核心团队来说,更容易实现,据说实现最小选择的代码也就几十行。 3) 更重要的是最小版本选择更容易实现可重现构建。试想一下,如果选择的是最大最新版本,那么针对同一份代码,其依赖包的最新最大版本在不同时刻可能是不同的,那么在不同时刻的构建,产生的最终文件就是不同的。 当然这一切的前提都是基于语义版本规范,对于不符合规范的module,相当于没有遵守契约,这套规则也就失效。这对任何语言来 说都是一样的。 4. 不属于。04讲说的那种情况是指在一个项目仓库中的不同目录下放置了多个go.mod,即一个项目中有多个module。

    共 3 条评论
    11
  • 丶能
    2021-10-25
    提问! “选择符合项目整体要求的最小版本”是可能选择,依赖最小版本与最新版本中任意版本,还是项目引入版本中的可选范围内的最小版本呢?

    作者回复: 按照课程中的例子,A 要求 >=C v1.1.0,B 要求 C >=v1.3.0,那么选择同时满足A与B要求的最小版本,就是v1.3.0。如果选择v1.1.0则不满足A要求。

    共 8 条评论
    10
  • 若水清菡
    2021-11-14
    之前看过两个老师讲go,上来基本上都适合先语法规则后实践的路线,每次go build都一脸懵逼,看下来还是不了解go的编译过程~~~~tony bai老师讲的非常好,遇到一位合适自己的老师,希望跟着学完这门课程,非常感谢老师~

    作者回复: 嗯嗯,这次的思路是先让大家上手。但作为入门课,后续必然也会对Go语法进行讲解与深入理解的。

    共 2 条评论
    9
  • Aaron Liu
    2021-10-25
    看起来有点像node module,初学者从go module开始构建会比较好,理解一个方式
    共 2 条评论
    6
  • 太匆匆
    2022-03-23
    默认是是最小版本原则,能否修改默认呢?比如举例当中的v1.1.0、v1.3.0、v1.7.0默认会选v1.3.0,开发者能否将其改成v1.7.0呢?

    作者回复: 好问题!可以通过go get xxx@v1.7.0显式更新go.mod中的依赖版本。或通过go mod edit命令或直接编辑go.mod进行。

    
    5
  • flexiver
    2022-03-13
    老师,您好。想要请问解答两个问题: 1、请问在构建module时, go mod init -module path, 这个module path 是固定要写成github.com/module name这样一个结构吗?

    作者回复: 好问题!不必要非得是github.com,也不必要非得是github.com/module name这样的。你可以使用module demo1这样的path。不过module path有三个作用,根据需要作出path的选择: 1. 定位代码仓库位置。如果你的代码是开源到一些公共代码托管站点,或者在组织内部的代码仓库时,path中要带上仓库的地址,比如github.com/repo/module,这样依赖你的module的其他代码可以找到你的module代码。 2. 如果你的module不在repo的根路径下,那么在module path中还要包含子目录路径。以github.com/etcd-io/etcd这个仓库为例。这个仓库下管理着多个go module。以其子目录raft下面的module为例,这个module的path为:module go.etcd.io/etcd/raft/v3。其中的raft就是子路径。 3. major版本号。如果major>=2,需要在module path中加上major号后缀。就像上面的module go.etcd.io/etcd/raft/v3。

    共 4 条评论
    5
  • 林
    2021-11-05
    这位老师的文章犹如编程爽文,看的过瘾

    作者回复: 哈哈,过奖了

    
    3
  • liaomars
    2021-10-26
    把GOPATH构建下的项目目录复制一份出来,在这个目录下面开启 GO MODULES,执行:go mod init && go mod tidy

    作者回复: 提示:go mod init后面要加上module path哦。

    共 2 条评论
    3
  • 功夫熊猫
    2021-11-15
    其实这几天我难受的是自定义的包导入的问题。

    作者回复: 这个go 1.18应该可以彻底解决。通过go.work。可以看看这个特性的前瞻:https://mp.weixin.qq.com/s/AGAz8dti8IwfVntOvBTUTg

    
    2
  • 郑童文
    2021-10-25
    请问老师go.mod中module path的最后一段,必须和module 的名称一样吗? 如果两者不同会出现什么情况? 谢谢!

    作者回复: 严格来说,go.mod中 module后面的路径就是module的“名字”。但go官方一般不提module “名字”这个概念。module是一个go包集合,更多是一个命名空间的概念。go module唯一的标识就是module path。所以不存在你说的不一致情况。

    
    2
  • 每天晒白牙
    2022-05-10
    老师,请教个问题,我是go1.16.4版本,按照老师在文中所说,GO111MODULE 应该为on呀,而我的确实空 (base) ➜ ~ go env GO111MODULE="" GOARCH="amd64" .... GOVERSION="go1.16.4"
    展开

    作者回复: 所谓默认为on是指,如果GO111MODULE没有显式设置,那么默认为on。你这里环境变量GO111MODULE为空,那么go编译器默认GO111module为on。

    共 2 条评论
    1
  • rocshen
    2022-03-06
    目前看过课程结构设计最合理的教程 给老师点赞

    作者回复: 感谢支持。

    
    1
  • 独钓寒江
    2021-12-25
    老师你好,最近 log4j 和 logback 都被发现了安全漏洞,很多Java程序都受到影响,修复起来工作量也不小。如果类似情况出现在Go方面,例如 logrus 出了安全漏洞,我们需要修改依赖版本, 我们可以怎么应对呢?可以简单说说吗?

    作者回复: 你这个问题很"与时俱进"啊:)。首先对于已经依赖logrus的版本的go项目,go build不会自动更新logrus到其最新版本,也就不会受到故意漏洞的侵害。假设你的项目在go mod init时获取到的是最新漏洞版本或通过go get logrus@latest获取到其漏洞版本,那么可以看一下07讲,将版本降级或升级到漏洞修复后的版本,操作步骤专栏里都有的。

    共 3 条评论
    1
  • 哈哈哈哈哈
    2021-11-17
    通过 go mod init 命令为这个项目创建一个 Go Module. $go mod init github.com/bigwhite/module-mode中 “github.com/bigwhite/module-mode“ ,为什么要加这个?这个什么意思?指代什么?

    作者回复: 专栏例子中使用github.com/user/repo这个样式作为module path是因为多数实用级module多是要上传到github上的。用这种示例便于后续与真实 生产接驳。但对于本地开发使用的简单示例程序而言,module path可以任意选用,比如: // go.mod module demo1 Go 1.17 也是ok的。

    共 2 条评论
    1
  • Darren
    2021-10-28
    老师,问一个目前遇到很奇怪的东西,没有一点思路: 是这样的,一个git项目,vendor模式,src目录下有多个项目(多个文件夹)中都有main.go,打包也是多个可执行文件,依赖同一个vendor,vendor也是在src目录下。 本地打包成的可执行文件,启动时报错,缺少配置文件。看代码逻辑确实是需要的,本机mac,设置了 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build 。 但是有一个以前开发同事的Linux机器,把src整个目录上传上去(包括vendor),然后解压后,执行打包,执行的可执行文件和本地打包的大小都不一样,同时执行就不报错了,打包机是基于Makefile,但是Makefile中感觉也没有写啥,与src目录同级,我关注项目的打包 export GOPATH=$(CURDIR) ; go build -o ./bin/xxxx .src/apps/xxxx; 按照老师这节课,我的理解是vendor一样的话,按道理打包的结果也是一样的(GOPATH也相同),为啥本地打包的就不行,打包机打包的就可以?老师有什么思路吗?谢谢老师
    展开
    共 4 条评论
    1
  • 一步
    2021-10-25
    怎么指定自己开发 moduel 的版本呢?

    作者回复: 别急,07讲会给你答案。

    共 3 条评论
    1
  • Harold
    2021-10-25
    语义版本不能算go mod创新吧

    作者回复: 语义版本是独立的。但go module的“语义导入版本”机制的确与众不同。

    
    1