凤岗仿做网站,南京制作网页速成班,seo是什么职位缩写,商标查询系统Golang 基础 Go Modules包管理
在 Go 项目开发中#xff0c;依赖包管理是一个非常重要的内容#xff0c;依赖包处理不好#xff0c;就会导致编译失败#xff0c;本文将系统介绍下 Go 的依赖包管理工具。
我会首先介绍下 Go 依赖包管理工具的历史#xff0c;并详细介绍下…Golang 基础 Go Modules包管理
在 Go 项目开发中依赖包管理是一个非常重要的内容依赖包处理不好就会导致编译失败本文将系统介绍下 Go 的依赖包管理工具。
我会首先介绍下 Go 依赖包管理工具的历史并详细介绍下目前官方推荐的依赖包管理方案 Go Modules。Go Modules 主要包括了 go mod 命令行工具、模块下载机制以及两个核心文件 go.mod 和 go.sum。
Go Modules 简介
Go Modules 是 Go 官方推出的一个 Go 包管理方案基于 vgo 演进而来具有下面这几个特性
可以使包的管理更加简单支持版本管理允许同一个模块多个版本共存可以校验依赖包的哈希值确保包的一致性增加安全性内置在几乎所有的 go 命令中包括go get、go build、go install、go run、go test、go list等命令。具有 Global Caching 特性不同项目的相同模块版本只会在服务器上缓存一份。
在 Go1.14 版本以及之后的版本Go 官方建议在生产环境中使用 Go Modules。
因此以后的 Go 包管理方案会逐渐统一到 Go Modules
Go 包管理的历史
在具体讲解 Go Modules 之前我们先看一下 Go 包管理的历史。从 Go 推出之后因为没有一个统一的官方方案所以出现了很多种 Go 包管理方案比较混乱也没有彻底解决 Go 包管理的一些问题。Go 包管理的历史如下图所示 这张图展示了 Go 依赖包管理工具经历的几个发展阶段:
Go1.5 版本前GOPATH
在 Go1.5 版本之前没有版本控制所有的依赖包都放在 GOPATH 下。采用这种方式无法实现包的多版本管理并且包的位置只能局限在 GOPATH 目录下。如果 A 项目和 B 项目用到了同一个 Go 包的不同版本这时候只能给每个项目设置一个 GOPATH将对应版本的包放在各自的 GOPATH 目录下切换项目目录时也需要切换 GOPATH这些都增加了开发和实现的复杂度。
Go1.5 版本Vendoring
Go1.5 推出了 vendor 机制并在 Go1.6 中默认启用。在这个机制中每个项目的根目录都可以有一个 vendor 目录里面存放了该项目的 Go 依赖包。
在编译 Go 源码时Go 优先从项目根目录的 vendor 目录查找依赖如果没有找到再去 GOPATH 下的 vendor 目录下找如果还没有找到就去 GOPATH 下找。这种方式解决了多 GOPATH 的问题但是随着项目依赖的增多vendor 目录会越来越大造成整个项目仓库越来越大。
在 vendor 机制下一个中型项目的 vendor 目录有几百 M 的大小一点也不奇怪。
Go1.9 版本Dep
Golang 依赖管理工具混乱的局面最终由官方来终结了Golang 官方接纳了由社区组织合作开发的 Dep作为 official experiment。在相当长的一段时间里Dep 作为标准成为了事实上的官方包管理工具。
Go1.11 版本之后Go Modules
Go1.11 版本推出了 Go Modules 机制Go Modules 基于 vgo 演变而来是 Golang 官方的包管理工具。在 Go1.13 版本Go 语言将 Go Modules 设置为默认的 Go 管理工具在 Go1.14 版本Go 语言官方正式推荐在生产环境使用 Go Modules并且鼓励所有用户从其他的依赖管理工具迁移过来。
Go 1.11 发布时候提到 This release adds preliminary support for a new concept called “modules,” an alternative to GOPATH with integrated support for versioning and package distribution. 此版本增加了对称为“模块”的新概念的初步支持这是 GOPATH 的替代方案集成了对版本控制和包分发的支持。 至此Go 终于有了一个稳定的、官方的 Go 包管理工具 下面再来介绍下 Go Modules 的使用方法。
包package和模块module
Go 程序被组织到 Go 包中Go 包是同一目录中一起编译的 Go 源文件的集合。在一个源文件中定义的函数、类型、变量和常量对于同一包中的所有其他源文件可见。模块是存储在文件树中的 Go 包的集合并且文件树根目录有 go.mod 文件。go.mod 文件定义了模块的名称及其依赖包通过导入路径和版本描述一个依赖。
模块和包的关系更像是集合和元素的关系包属于模块一个模块是零个或者多个包的集合。下面的代码段引用了一些包
import (// Go 标准包fmt// 第三方包github.com/spf13/pflag// 匿名包_ github.com/jinzhu/gorm/dialects/mysql// 内部包github.com/marmotedu/iam/internal/apiserver
)这里的fmt、http://github.com/spf13/pflag和http://github.com/marmotedu/iam/internal/apiserver都是 Go 包。 Go 中有 4 种类型的包
Go 标准包在 Go 源码目录下随 Go 一起发布的包。第三方包第三方提供的包比如来自于 http://github.com 的包。匿名包只导入而不使用的包。通常情况下我们只是想使用导入包产生的副作用即引用包级别的变量、常量、结构体、接口等以及执行导入包的init()函数。内部包项目内部的包位于项目目录下。
下面的目录定义了一个模块
$ ls hello/
go.mod go.sum hello.go hello_test.go worldhello 目录下有一个 go.mod 文件说明了这是一个模块该模块包含了 hello 包和一个子包 world。该目录中也包含了一个 go.sum 文件该文件供 Go 命令在构建时判断依赖包是否合法。
Go Modules 命令
Go Modules 的管理命令为go modgo mod有很多子命令你可以通过go help mod来获取所有的命令。
下面我来具体介绍下这些命令。
download下载 go.mod 文件中记录的所有依赖包。edit编辑 go.mod 文件。graph查看现有的依赖结构。init把当前目录初始化为一个新模块。tidy添加丢失的模块并移除无用的模块。默认情况下Go 不会移除 go.mod 文件中的无用依赖。当依赖包不再使用了可以使用go mod tidy命令来清除它。vendor将所有依赖包存到当前目录下的 vendor 目录下。verify检查当前模块的依赖是否已经存储在本地下载的源代码缓存中以及检查下载后是否有修改。why查看为什么需要依赖某模块。
Go Modules 开关
如果要使用 Go Modules在 Go1.14 中仍然需要确保 Go Modules 特性处在打开状态。
你可以通过环境变量 GO111MODULE 来打开或者关闭。
GO111MODULE 有 3 个值
auto在 Go1.14 版本中是默认值在$GOPATH/src下且没有包含 go.mod 时则关闭 Go Modules其他情况下都开启 Go Modules。on启用 Go ModulesGo1.14 版本推荐打开未来版本会设为默认值。off关闭 Go Modules不推荐。
所以如果要打开 Go Modules建议直接设置export GO111MODULEon。
go.mod 和 go.sum 介绍
go.mod 文件是 Go Modules 的核心文件。下面是一个 go.mod 文件示例
module github.com/marmotedu/iamgo 1.17require (github.com/AlekSi/pointer v1.1.0github.com/appleboy/gin-jwt/v2 v2.6.3github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535github.com/gin-gonic/gin v1.6.3github.com/golangci/golangci-lint v1.30.0 // indirectgithub.com/google/uuid v1.0.0github.com/blang/semver v3.5.0incompatiblegolang.org/x/text v0.3.2
)replace (github.com/gin-gonic/gin /home/colin/gingolang.org/x/text v0.3.2 github.com/golang/text v0.3.2
)exclude (github.com/google/uuid v1.1.0
)
go.mod 语句
go.mod 文件中包含了 4 个语句分别是 module、require、replace 和 exclude。
module用来定义当前项目的模块路径。go用来设置预期的 Go 版本目前只是起标识作用。require用来设置一个特定的模块版本格式为导入包路径 版本 [// indirect]。exclude用来从使用中排除一个特定的模块版本如果我们知道模块的某个版本有严重的问题就可以使用 exclude 将该版本排除掉。replace用来将一个模块版本替换为另外一个模块版本。格式为 $module n e w m o d u l e newmodule newmodulenewmodule可以是本地磁盘的相对路径例如http://github.com/gin-gonic/gin ./gin。也可以是本地磁盘的绝对路径例如http://github.com/gin-gonic/gin /home/lk/gin。还可以是网络路径例如http://golang.org/x/text v0.3.2 http://github.com/golang/text v0.3.2。 这里需要注意虽然我们用 n e w m o d u l e 替换了 newmodule替换了 newmodule替换了module但是在代码中的导入路径仍然为$module。 replace 在实际开发中经常用到下面的场景可能需要用到 replace 在项目开发初期A 项目依赖 B 项目的包但 B 项目因为种种原因没有 push 到仓库这时也可以在 go.mod 中把依赖包替换为 B 项目的本地磁盘路径。 在国内访问 http://golang.org/x 的各个包都需要翻墙可以在 go.mod 中使用 replace替换成 GitHub 上对应的库例如http://golang.org/x/text v0.3.0 http://github.com/golang/text v0.3.0。
go.mod 版本号
go.mod 文件中有很多版本号格式这里我来详细说明一下。
如果模块具有符合语义化版本格式的 tag会直接展示 tag 的值例如 http://github.com/AlekSi/pointer v1.1.0除了 v0 和 v1 外主版本号必须显试地出现在模块路径的尾部例如http://github.com/appleboy/gin-jwt/v2 v2.6.3。
• 对于没有 tag 的模块Go 命令会选择 master 分支上最新的 commit并根据 commit 时间和哈希值生成一个符合语义化版本的版本号例如http://github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535。
• 如果模块名字跟版本不符合规范例如模块的名字为http://github.com/blang/semver但是版本为 v3.5.0正常应该是http://github.com/blang/semver/v3go 会在 go.mod 的版本号后加incompatible表示。
• 如果 go.mod 中的包是间接依赖则会添加// indirect注释例如http://github.com/golangci/golangci-lint v1.30.0 // indirect。
这里再详细介绍下出现// indirect的情况:
原则上 go.mod 中出现的都是直接依赖但是下面的情况只要出现,就会在 go.mod 中添加间接依赖。 如果模块 A 依赖模块 B模块 B 依赖 B1 和 B2但是 B 没有 go.mod 文件则 B1 和 B2 会记录到 A 的 go.mod 文件中并在最后加上// indirect。 参考
https://zhuanlan.zhihu.com/p/635696935