Go 包管理历史以及 Go mod 使用
之前也写过 Go 管理依赖工具 godep 的使用,当时看 godep 使用起来还是挺方便,其原因主要在于有总比没有强。关于依赖管理工具其实还是想从头聊聊这个需求以及大家做这个功能的各种出发点。
GOPATH 和 GOROOT
GOROOT 这个变量的作用就是为了告诉当前运行的 Go 进程当前 Go 安装在哪里,当你想要运行的时候去哪里找 Go SDK相关的类。
GOPATH 这个设定其实从语言层面上来说就有点反设计模式。主要原因在于 Go 刚出生的时候没有自带包管理功能,默认所有的项目和引用的第三方包都下载到 src 目录下,第三方包对于 Go 而言也是一个可用的Go项目,这一点跟 Java 区别还是挺大。
那 GOPATH 是否可以设定多个呢?当让可以,并且人家就是这么让你用的。
比如你有项目A,设定的 GOPATH 是:/src/projA/,那么A项目所有的依赖都会下载在这个目录。
比如你有项目B,设定的 GOPATH 是:/src/projB/,那么B项目所有的依赖都会下载在这个目录。
而如果你使用同一个 GOPATH,那么你所有的项目 down 下来的依赖都在 src 目录下,这时候就有版本的问题,如果 A 项目想用 依赖 C 的 1.0.0 版本,B 项目想用依赖 C 的 1.0.1 版本,这时候该怎么指定呢?
正确的 GOPATH 就是一个项目一个。
包管理历史上的“恩怨情仇”
所以问题来了,一个项目一个 GOPATH,如果我有三个项目是个超级无敌大系统,可能会 down 下来整个 github 上的开源库,那这存储都是问题啊。
针对 Go 官方对包管理的乱象无动于衷,社区的同学们实在是忍无可忍,相继开源了各种包管理工具。
13 年的时候 Godep 诞生,原理很简单,跟 maven 一样,指定一个统一的包管理目录,将这个目录作为唯一的 GOPATH。也是在这一年 Docker 开始火起来了, Go 作为微服务开发“非官方指定语言”被广泛使用,同时矮子里面拔高子, dep 也绽放异彩。
15年 Go 官方被社区倒逼,1.5 版本同步带上了一个实验性质的功能 vendor 机制。由于默认情况下是关闭态,所以 normal 用户无感知,super 用户才会去体验。1.6 版本的时候该功能才打开为正常使用状态。
那么什么是 vendor 机制呢?简单说就是在你的项目中包含一个 vendor 的文件夹,它代替了通用的 GOPATH 目录,你项目中所有的依赖都会在这里存在。有了 vendor 好处显而易见,你再也不用手工修改 GOPATH 了,每个人运行环境的包也都统一了起来。
但是问题也随之而来,如果你依赖的一个项目也使用 vendor 管理依赖,那不就形成了嵌套依赖了吗?这种情况怎么处理。
当然还有别的各种问题,总之这也不是一个能拿得出手的成熟方案。
后面又出了 glide 等等社区的方案,但是到这里为止官方还是没有出一个能一统天下的方案。
当时 Google 官方的 Go 负责人 Russ 其实是有和 dep 的开发者 Sam 沟通让他修改一些设计以便将 dep 继承到 go 命令中去,但是由于一些观念上的原因 Sam 并没有接受,所以也因此错过一段良缘。
后面官方由于社区的压力在Go 1.11 版本的时候集成了新特性 Go modules。这是首次以官方名义开发的包管理工具。Go 命令直接支持 modules 相关用法。
Go modules
Go modules 是官方推出的依赖管理工具,Go modules 提供了3个重要的功能:
go.mod文件,它与package.json或Pipfile文件的功能类似。- 机器生成的传递依赖项描述文件 :
go.sum。 - 不再有 GOPATH 限制。模块可以位于任何路径中。
要想使用 Go modules 请升级到 1.11 及其以上版本,1.13 版本已经默认开启 Go modules,如果想体验这个功能建议将 Go 版本升级到 1.13。
另外,这个功能默认并不是开启的,需要手动设置环境变量开开启:
go env -w GO111MODULE=on
go env -w 是Go 1.13 新增的命令,用于写入环境变量。写入的地方是os.UserCOnfigDir所在的目录。
GO111MUDULE 变量是 Go modules 的开关。 有以下几个参数:
- auto:如果项目包含了 go.mod 文件,则启用 Go modules 功能。在Go 1.13 中是默认值。
- on:始终开启 Go modules。
- off:禁用 Go modules。
使用
首先我们在 GOPATH外新建一个目录,比如 usr/local/mod-demo,进入目录下,生成 go.mod文件:
Go mod init mod-demo
打开我们刚生成的 go.mod 文件可以看到:
module mod-demo
go 1.13
go.mod 文件是开启 modules 的必备配置文件。它记录了当前项目引用的包数据信息。go.mod 文件中定义了以下关键词:
- module:用于定义当前项目的模块路径
- go:用于设置Go 版本信息
- require:用于设置一个特定的模块版本
- exclude:用于从使用中排除一个特定的模块版本
- replace:用于将一个模块版本替换为另一个模块版本
接下来添加项目外的依赖:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
d := gin.Default()
d.GET("/index", func(c *gin.Context) {
c.JSON(200, gin.H{"message":"hello world","data":""})
})
d.Run("127.0.0.1:8080")
}
上面我们添加了 gin 的依赖包,然后看 go.mod 的内容:
module mod-demo
go 1.15
require github.com/gin-gonic/gin v1.6.3
可以看到已经添加了依赖。
以下版本格式都是合法的:
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
gopkg.in/vmihailenco/msgpack.v2 v2.9.1
gopkg.in/yaml.v2 <=v2.2.1
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
latest
Go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,详见 Modules官方介绍。 Go 会自动生成一个 go.sum 文件来记录 dependency tree:
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
......
......
......
这里会记录当前以来的依赖包所依赖的依赖。
另外,之前我们在 golang.org 上下载不下来的包,直接使用 github 上的镜像,使用 module 之后可以使用 replace 关键字来个替换:
比如你之前引用是这样的:
require (
golang.org/x/text v0.3.2
)
那么使用 replace 之后就是这样的:
require (
golang.org/x/text v0.3.2
)
replace golang.org/x/text v0.3.2 => github.com/golang.org/text v0.3.2
执行命令go list -m all 也可以查看当前所有依赖项。
Go modules 有如下常用命令:
download download modules to local cache (下载依赖的module到本地cache))
edit edit go.mod from tools or scripts (编辑go.mod文件)
graph print module requirement graph (打印模块依赖图))
init initialize new module in current directory (再当前文件夹下初始化一个新的module, 创建go.mod文件))
tidy add missing and remove unused modules (增加丢失的module,去掉未用的module)
vendor make vendored copy of dependencies (将依赖复制到vendor下)
verify verify dependencies have expected content (校验依赖)
why explain why packages or modules are needed (解释为什么需要依赖)
GOPROXY加速
Go 很多包都会被 GFW 墙掉,现在有了Go modules 就可以解决了。
Go modules 支持配置镜像源,从而更快更稳定的完成 go get 操作,大家只需要 export GOPROXY 配置一下即可,推荐的源地址:https://goproxy.io/。
Go 包管理历史以及 Go mod 使用的更多相关文章
- python包管理历史
1.标准库工具distutils,2000年发布,是包安装和发布工具 setup.python 程序,利用distutils 开发 示例: python setup.py install 安装一个包 ...
- 包管理神器-pipenv
一:前言 介绍一个包管理神器-pipenv,这个工具可以让我们在写代码.创建Python运行环境.package依赖关系以及项目合作的时候更有效率. 在pycon2018上,Kenneth Reitz ...
- Go包管理go mod使用
Go Modules介绍 为了解决Go包管理的问题,Go从1.11开始加入了Go Modules这一新特性.让包的依赖和版本管理更加容易. 一个module可以理解为一个单独的包或者模块,module ...
- 拜拜了,GOPATH君!新版本Golang的包管理入门教程
Go 1.11和1.12实现了对包管理的初步支持,Go的新依赖管理系统使依赖版本信息明确且易于管理.Using Go Modules - The Go Blog 新的包管理模式有什么不同? 作为Go语 ...
- Golang 包管理机制
Golang 包管理机制 1. 历史 在go1.11之前, 并没有官方的包管理机制(Godep算个半官方), 主流的包管理机制有: GoVendor Glide Godep 在go1.11之后, 官方 ...
- 层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10
Go lang使用包(package)这种概念元素来统筹代码,所有代码功能上的可调用性都定义在包这个级别,如果我们需要调用依赖,那就"导包"就行了,无论是内部的还是外部的,使用im ...
- Helm包管理
Helm Kubernetes 包管理工具 Helm 可以帮助我们管理 Kubernetes 应用程序 - Helm Charts 可以定义.安装和升级复杂的 Kubernetes 应用程序,Char ...
- Linux程序包管理之rpm
rpm简介 rpm( Red Hat Package Manager )是一个开放的软件包管理系统.它工作于Red Hat Linux及其他Linux系统,成为Linux中公认的软件包管理标准. rp ...
- Linux程序包管理之yum及源代码安装
第十六章.Linux程序包管理之yum及源代码安装 目录 yum介绍 yum配置文件 yum的repo配置文件中可用的变量 yum命令的使用 使用光盘作为本地yum仓库 如何创建yum仓库 编译安装的 ...
随机推荐
- JVM学习(八)指令重排序
一.数据依赖性 在学习JVM的指令重排序之前,我们先了解一下什么是数据依赖性: 编译器和处理器在处理具体的指令时,可能会对操作进行重排序来提高执行性能[多条指令并行执行,所以提升性能的同时也可能会导致 ...
- 阅读源码的利器——Intellij-IDEA-Replace-in-Path-使用技巧
前言 讲讲宇宙排名第二的开发工具-–IDEA的使用技巧. 搜索/替换 技巧 阅读源码的利器 1.Match case: 如果勾选该按钮,搜索时将区分大小写字母. 2.Preserve case ...
- 有关图的连通性的Tarjan算法
割点与桥 在一个无向连通图中,若将某个点及其相连的边删除后,图就不连通了,则这样的点被称为割点. 在一个无向连通图中,若将某条边删除后,图就不连通了,则这样的边被称为割边,即桥. 在一张图中求出割点或 ...
- springmvc 源码分析(一)-- DisparcherServlet的创建和注册到tomcat
一. servlet 3.0 的使用 1.1 环境搭建: servlet跟spring没有任何关系,我创建一个servlet可以不依赖spring,现在搭建一个纯的servlet项目,并实现简单的类似 ...
- Python-__init__ 和 __new__区别和原理
__init__ 和 __new__区别 1. 从传递参数角度看,__init__第一个参数是实例本身, __new__传递的是类本身 2. 从执行顺序角度看,__new__方法执行在 __init_ ...
- SQLMAP注入Access数据库
今天偶遇一Access数据库 1.首先尝试是否存在注入点,and1=1,and 1=2,发现返回信息不一样 2.使用sqlmap脱裤,发现时Access数据库,不能提权, 3.那就直接暴库吧,sqlm ...
- OneWire应用 单总线温度传感器DS18系列
OneWire DS18S20, DS18B20, DS1822 Temperature DS18B20 The DS18B20 digital thermometer provides 9-bit ...
- DDOS、CC、sql注入,跨站攻击防御方法
web安全常见攻击解读--DDos.cc.sql注入.xss.CSRF 一,DDos https://www.cnblogs.com/sochishun/p/7081739.html#4111858 ...
- 非阻塞I/O和阻塞I/O
1.简介 等待队列实现在事件上的条件等待:希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权.可用于: - 中断处理 - 进程同步 - 定时 2.等待队列头数据结构 1 typedef st ...
- pytest文档42-fixture参数化params
前言 参数化是自动化测试里面必须掌握的一个知识点,用过 unittest 框架的小伙伴都知道使用 ddt 来实现测试用例的参数化. pytest 测试用例里面对应的参数可以用 parametrize ...