参考:一文搞懂 Go Modules 前世今生及入门使用

前言

在说他们之间的区别时,我们需要先了解了解GoMoudle的前世今生。
以前,Go 语言的的包依赖管理一直都被大家所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的 GOPATH 到 GO VENDOR,再到最新的 GO Modules,虽然走了不少的弯路,但最终还是拿出了 Go Modules 这样像样的解决方案。

目前最主流的包依赖管理方式是使用官方推荐的 Go Modules ,在版本 Go 1.14 发布后,官方正式放话,强烈推荐你使用 Go Modules,并且有自信可以用于生产中。

本文会大篇幅的讲解 Go Modules 的使用,但是在那之前,我仍然会简要介绍一下前两个解决方案 GOPATH 和 go vendor 到底是怎么回事?我认为这是有必要的,因为只有了解它的发展历程,才能知道 Go Modules 的到来是有多么的不容易,多么的意义非凡。

一、 最开始使用的GOPATH

GOPATH 应该很多人都很眼熟了,之前在配置环境的时候,都配置过吧?

你可以将其理解为工作目录,在这个工作目录下,通常有如下的目录结构

bin:存放编译后生成的二进制可执行文件
pkg:存放编译后生成的 .a 文件
src:存放项目的源代码,可以是你自己写的代码,也可以是你 go get 下载的包
将你的包或者别人的包全部放在 $GOPATH/src 目录下进行管理的方式,我们称之为 GOPATH 模式。

在这个模式下,使用 go install 时,生成的可执行文件会放在 $GOPATH/bin 下
如果你安装的是一个库,则会生成 .a 文件到 $GOPATH/pkg 下对应的平台目录中(由 GOOS 和 GOARCH 组合而成),生成 .a 为后缀的文件。

GOOS,表示的是目标操作系统,有 darwin(Mac),linux,windows,android,netbsd,openbsd,solaris,plan9 等
而 GOARCH,表示目标架构,常见的有 arm,amd64 等

这两个都是 go env 里的变量,你可以通过 go env 变量名 进行查看

至此,你可能不会觉得上面的方案会产生什么样的问题,直到你开始真正使用 GOPATH 去开发程序,就不得不开始面临各种各样的问题,其中最严重的就是版本管理问题,因为 GOPATH 根本没有版本的概念。

以下几点是你使用 GOPATH 一定会碰到的问题:
你无法在你的项目中,使用指定版本的包,因为不同版本的包的导入方法也都一样
其他人运行你的开发的程序时,无法保证他下载的包版本是你所期望的版本,当对方使用了其他版本,有可能导致程序无法正常运行
在本地,一个包只能保留一个版本,意味着你在本地开发的所有项目,都得用同一个版本的包,这几乎是不可能的。

二、go vendor 模式的过渡

为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 开始支持 vendor 。

以前使用 GOPATH 的时候,所有的项目都共享一个 GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 模式下只能有一个版本的第三方库。
解决的思路就是,在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5 的 Go 在你设置了开启 GO15VENDOREXPERIMENT=1 (注:这个变量在 v1.6 版本默认为1,但是在 v1.7 后,已去掉该环境变量,默认开启 vendor 特性,无需你手动设置)后,会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。

其搜索包的优先级顺序,由高到低是这样的

  • 当前包下的 vendor 目录
  • 向上级目录查找,直到找到 src 下的 vendor 目录
  • 在 GOROOT 目录下查找
  • 在 GOPATH 下面查找依赖包

虽然这个方案解决了一些问题,但是解决得并不完美。

如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。

并且如果要分享开源你的项目,你需要将你的所有的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如你预期那样正常运行。

这些看似不是问题的问题,会给我们的开发使用过程变得非常难受,虽然我是初学者,还未使用过 go vendor,但能有很明显的预感,这个方案照样会另我崩溃。

三、go mod 的粉墨登场

go modules 在 v1.11 版本正式推出,在最新发布的 v1.14 版本中,官方正式发话,称其已经足够成熟,可以应用于生产上。

从 v1.11 开始,go env 多了个环境变量: GO111MODULE ,这里的 111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,比如当初 vendor 出现的时候,也多了个 GO15VENDOREXPERIMENT环境变量,其中 15,表示的vendor 是在 v1.5 时才诞生的。

GO111MODULE 是一个开关,通过它可以开启或关闭 go mod 模式。

它有三个可选值:off、on、auto,默认值是auto。

GO111MODULE=off禁用模块支持,编译时会从GOPATH和vendor文件夹中查找包。
GO111MODULE=on启用模块支持,编译时会忽略GOPATH和vendor文件夹,只根据 go.mod下载依赖。
GO111MODULE=auto,当项目在$GOPATH/src外且项目根目录有go.mod文件时,自动开启模块支持。

go mod 出现后, GOPATH(肯定没人使用了) 和 GOVENDOR 将会且正在被逐步淘汰,但是若你的项目仍然要使用那些即将过时的包依赖管理方案,请注意将 GO111MODULE 置为 off。

具体怎么设置呢?可以使用 go env 的命令,如我要开启 go mod ,就使用这条命令

$ go env -w GO111MODULE="on"

四、go mod 依赖的管理

1、go mod 不再依靠 $GOPATH,使得它可以脱离 GOPATH 来创建项目
2、对项目进行 go modules 的初始化
3、接下来很重要的一点,我们要看看 go install 把下载的包安装到哪里了?

在使用 go modules 模式后,项目目录下会多生成两个文件也就是 go.mod 和 go.sum 。
这两个文件是 go modules 的核心所在,这里不得不好好介绍一下。

①、go.mod 文件

go.mod 的内容比较容易理解
第一行:模块的引用路径
第二行:项目使用的 go 版本
第三行:项目所需的直接依赖包及其版本

在实际应用上,你会看见更复杂的 go.mod 文件,比如下面这样

module github.com/BingmingWong/module-testgo 1.14require (example.com/apple v0.1.2example.com/banana v1.2.3example.com/banana/v2 v2.3.4example.com/pear // indirectexample.com/strawberry // incompatible
)exclude example.com/banana v1.2.4
replace(golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dacgolang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638dgolang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
)

主要是多出了两个 flag:

exclude: 忽略指定版本的依赖包
replace:由于在国内访问golang.org/x的各个包都需要f.q,你可以在go.mod中使用replace替换成github上对应的库。

②、go.sum 文件

反观 go.sum 文件,就比较复杂了,密密麻麻的。

可以看到,内容虽然多,但是也不难理解

每一行都是由 模块路径,模块版本,哈希检验值 组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash 是以h1:开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。

值得注意的是,为什么有的包只有一行

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

而有的包却有两行呢

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

那些有两行的包,区别就在于 hash 值不一行,一个是 h1:hash,一个是 go.mod h1:hash

而 h1:hash 和 go.mod h1:hash两者,要不就是同时存在,要不就是只存在 go.mod h1:hash。那什么情况下会不存在 h1:hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的h1 hash,就会出现不存在 h1 hash,只存在 go.mod h1:hash 的情况。

go.mod 和 go.sum 是 go modules 版本管理的指导性文件,因此 go.mod 和 go.sum 文件都应该提交到你的 Git 仓库中去,避免其他人使用你写项目时,重新生成的go.mod 和 go.sum 与你开发的基准版本的不一致。

五、go mod 命令的使用

go mod init:初始化go mod, 生成go.mod文件,后可接参数指定 module 名,上面已经演示过。
go mod download:手动触发下载依赖包到本地cache(默认为$GOPATH/pkg/mod目录)
go mod graph: 打印项目的模块依赖结构
go mod tidy :添加缺少的包,且删除无用的包
go mod verify :校验模块是否被篡改过
go mod why: 查看为什么需要依赖
go mod vendor :导出项目所有依赖到vendor下
go mod edit :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 -require=golang.org/x/text 添加依赖,接 -droprequire=golang.org/x/text 删除依赖,详情可参考 go help mod edit
go list -m -json all:以 json 的方式打印依赖详情

如何给项目添加依赖(写进 go.mod)呢?
有两种方法:
你只要在项目中有 import,然后 go build 就会 go module 就会自动下载并添加。
自己手工使用 go get 下载安装后,会自动写入 go.mod 。

六、区别

在上述分析了那么多之后,我个人总结出来(有可能个人理解不对,如果大家有更好的见解,麻烦给我讲一下,谢谢),两者的区别就是:

GOPATH模式 gomod模式
必须设置GOPATH环境变量 可设可不设,有默认
$GOPATH/pkg目录存放 .a 文件 $GOPATH/pkg目录存放依赖包
$GOPATH/src下存放项目的源代码,可以是你自己写的代码,也可以是你 go get 下载的包 可以放源代码文件,也可以不用
源代码文件必须放$GOPATH/src下 源代码文件放任何地方
无法对依赖包作版本控制 可以对依赖包作版本控制

七、总结写在最后

如果让我用一段话来评价 GOPATH 和 go vendor,我会说

GOPATH 做为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,但是还不够好用,直到 go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher 都统一比较满意的方案,达到了能用且好用的标准。

如果是刚开始学习 Golang ,那么 GOPATH 和 go vendor 可以做适当了解,不必深入研究,除非你要接手的项目由于一些历史原因仍然在使用 go vender 械管理,除此之外,任何 Gopher 应该从此刻就投入 go modules 的怀抱。

golang中使用GOPATH模式和GoModule(gomod)模式的区别相关推荐

  1. Golang中的GoPath和GoModule

    文章目录 什么是GoPath? 什么是GoModule? GoModule的设置 GoModule无法下载国外的依赖包问题 在Golang中,有两个概念非常容易弄错,第一个就是GoPath,第二个则是 ...

  2. c++除法保留小数_Golang中除法和取模运算与Python3的区别

    Golang中除法和取模运算与Python3的区别 个人主页:[https://polar9527.github.io] 除法的取整分为三类:向上取整.向下取整.向零取整. 向上取整:向+∞ 方向取最 ...

  3. php协程和goroutine,golang中四种方式实现子goroutine与主协程的同步

    如何实现子goroutine与主线程的同步 第一种方式:time.sleep(),这种方式很太死板,就不演示了. 第二种方式:使用channel机制,每个goroutine传一个channel进去然后 ...

  4. golang中tcp socket粘包问题和处理

    http://www.01happy.com/golang-tcp-socket-adhere/ 在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据交互格 ...

  5. golang中的包管理工具——govendor和godep简单学习

    为什么用vendor目录 依赖问题 我们知道,一个工程稍大一点,通常会依赖各种各样的包.而Go使用统一的GOPATH管理依赖包,且每个包仅保留一个版本.而不同的依赖包由各自的版本工具独立管理,所以当所 ...

  6. golang中builtin包说明

    builtin包是go的预声明定义,包括go语言中常用的各种类型和方法声明,包括变量和常量两部分.其详细声明在builtin.go文件中,链接:http://golang.org/src/builti ...

  7. golang中的图像image处理详解

    常用的图像格式有png,jpeg,gif,对应的文件后缀为png,jpg,gif,当然文件的具体内容编码才能证明存放的是哪种文件,图像文件的头部都存储了具体标志,比如jpeg前缀\xffd8,png前 ...

  8. Golang并发模型:轻松入门流水线FAN模式

    前一篇文章<Golang并发模型:轻松入门流水线模型>,介绍了流水线模型的概念,这篇文章是流水线模型进阶,介绍FAN-IN和FAN-OUT,FAN模式可以让我们的流水线模型更好的利用Gol ...

  9. golang中的信号量的实现原理

    概述 我们前面讲过 操作系统的信号量,以及 golang中的Mutex原理解析,就抛出了一个问题,操作系统的信号量的管理对象是线程,而 Mutex 中使用的信号量是针对协程的,那么这就意味着golan ...

最新文章

  1. java进阶 线程池 join用法总结:thread4.join();方法,就表明thread4.join();这个线程受到贵客待遇,直到这个线程执行完,被插入这个方法的载体线程才可以执行。
  2. java基础知识回顾之java Thread类学习(七)--java多线程安全问题(死锁)
  3. 4.14Python数据处理篇之Matplotlib系列(十四)---动态图的绘制
  4. android炫酷动画代码,Android高级UI特效仿直播点赞动画效果
  5. 行为设计模式 - 观察者设计模式
  6. Android学习随笔 -- draw9patch的使用
  7. 数据结构 红黑树(RBTree)的原理与实现
  8. 【pycharm】pycharm上安装tensorflow,报错:AttributeError: module ‘pip‘ has no attribute ‘main‘ 解决方法
  9. Fiddler工具中显示服务器IP地址的方法
  10. 只知道人工智能远远不够 下一件大事将是边缘计算!
  11. 动态规划实战3-leetcode 55.Jump Game
  12. Docker教程精要版
  13. 《Mybatis 手撸专栏》第10章:使用策略模式,调用参数处理器
  14. M0、M1、M2、M3
  15. 前段时间《八佰》上映了,让我们看看网友们对这部电影有什么看法
  16. 电梯广告框尺寸批发供应|电梯相框广告规格
  17. 英菲克I9_H8_当贝纯净桌面-YYF定制-线刷固件包
  18. SunOne中的domian常用命令
  19. 这些两轮电动车的黑科技你知道吗?
  20. 发现 3 个已存在的 RPM 数据库问题, 'yum check' 输出如下 解决办法

热门文章

  1. 数字孪生技术在校园能够发挥什么样的作用?
  2. 使用go-cqhttp结合python作自己的机器人(下)
  3. WebForm Combox下拉框联动
  4. TensorFlow下如何将图片制作成数据集
  5. 安卓手机游戏测试要点
  6. 一年前的王信文谈《刀塔传奇》(下)
  7. html5 video 在线视频,HTML5 视频(Video)
  8. 微信公众号开发之客服功能的群发消息功能
  9. 关于 Serif Sans-Serif Monospace 字体
  10. 计算机毕业设计Node.js+Vue基于html的网上购物系统(程序+源码+LW+部署)