Golang模块管理功能
文章目录
-
1. Golang 包管理
-
- 1.1 GOPATH 包管理
- 1.2 Go vendor 包管理
- 1.3 Go modules包管理
-
2. Go Modules 应用实践
-
-
2.1 Go modules关键信息
-
- 2.1.1 go mod 命令行
- 2.1.2 配置代理服务
-
2.2 创建项目
-
2.3 获取依赖包
-
2.4 运行项目
-
1. Golang 包管理
1.1 GOPATH 包管理
第一阶段: 在Golang早期阶段,通过设置GOPATH变量来确定项目的起始位置。位于GOPATH目录子目录中的src文件夹中存储了项目的全部代码。所有模块均存放在src文件夹内。常见的项目代码组织结构如下所示:
GOPATH
|----bin
|----pkg
|----src
|--------packageA
|--------packageB
|--------github.com
|------------accountC
|----------------moduleC1
|----------------moduleC2
|------------accountD
|----------------moduleD1
|----------------moduleD2
代码解读
随着Go语言生态日益活跃, 越来越多的应用开始采用Go语言进行开发, 这些应用所涉及的业务范围不断扩大, 从而导致系统的复杂度持续上升. 随着协作人数显著增加, 各类开发者对于Go语言项目的代码管理和维护要求不断提高. 如果一个开发者同时负责8个不同项目, 在src目录下包含了这8个项目以及它们的所有依赖项都放置在一起的情况下, 这将使得开发者难以迅速识别每个项目的独立依赖关系. 当不同项目引用同一库的不同版本时, 这种基于GOPATH的设计方案将面临挑战.
1.2 Go vendor 包管理
第二阶段: 采用Go vendor进行模块化管理策略,在每个模块新增一个vendor子目录,并将本项目所需的第三方库文件系统地放置于该模块所属项目的根目录下的vendor子文件夹内。通过这种方式确保各个独立项目能够自主维护其所需的依赖版本。代码架构如下:
GOPATH
|----bin
|----pkg
|----src
|--------github.com
|------------accountA
|----------------projectA
|--------------------sourcecodeA
|--------------------vendor
|------------------------github.com/xxxxx/xxxxxx(V1)
|------------------------github.com/xxxxx/yyyyyy(V2)
|----------------projectB
|--------------------sourcecodeB
|--------------------vendor
|------------------------github.com/xxxxx/xxxxxx(V2)
|------------------------github.com/xxxxx/yyyyyy(V3)
代码解读
Project A与Project B共同依赖github.com/xxxxx/xxxxxx及github.com/xxxxx/yyyyyy两组软件包。然而,在具体应用中发现Project A分别使用了V1及V2两个版本软件包配置文件(即V1配置文件与V2配置文件),而Project B则主要采用了V2及V3这两个版本配置文件以实现不同需求目标。为了实现对各项目所使用的不同版本软件包的有效管理,在每个项目的根目录下的vendor目录中组织并存储相应项目的相关软件包副本,并通过这种方式实现了对各项目所使用的不同版本软件包的有效自主管理目的。采用此方法进行软件组件管理后会带来一定的负面影响:如果我们的项目引用了大量外部组件,则会导致整个项目的最终打包体积极大程度上增大,并且首次上传Git代码仓库时还需要投入更多的时间成本进行处理操作。此外,在某些情况下可能会遇到以下问题:某些开发环境或协作规范规定禁止将vendor目录下的组件提交至Git代码仓库中存放(即禁止将这些组件作为公共资源供他人访问)。因此,在这种情况下如果其他人下载了我们的GitHub项目源码,则可能会遇到以下问题:当他们使用命令行工具运行go get -u指令时所获取到的实际组件版本与我们选择的初始提交组件可能存在差异性(即存在不一致的风险),从而可能导致程序运行出现异常情况。
1.3 Go modules包管理
在第三阶段中采用Go Modules来进行项目的模块化管理。从Golang 1.28版本起,默认引入了go mod功能以实现项目的模块化管理。建议手动配置环境变量GO[STABLE]__MODULES参数设为'on'以启用此功能,默认情况下该参数会被自动配置。若需手动配置环境变量GO[STABLE]__MODULES,请通过以下命令执行:
go env -w GO111MODULE=on
代码解读
- 当启用Go Modules时(即设置为
GO111MODULE=on),系统会启用Go模块化的包管理功能。在这种模式下,在搜索依赖项时不再检查位于GOPATH/src目录中的组件。 - 关闭Go模块(即设置为
GO111MODULE=off),系统会在搜索依赖项时首先检查项目根目录下的vendor目录是否存在相应的组件;如果未找到,则会继续搜索位于GOPATH/src目录中的组件。 - 设置为默认模式(即设置为
GO111MODULE=auto)时,在以下两种情况下会启用Go模块:- 当当前项目的文件位于`$GOPATH/src/```目录中;
- 否则如果存在对应的
.go.mod文件,则会自动启用模块化管理功能。
通过启用Go Modules进行包管理,我们的项目彻底摆脱了对GOPATH的依赖,并真正地实现了无论在哪里都可以运行我们的程序的目标。项目的整体架构如下:
|--D:\
|----projectA
|--------go.mod
|--------sourcecodeA
|--E:\
|----projectB
|--------go.mod
|--------sourcecodeB
|----demo
|--------test
|------------go.mod
|------------testcode
代码解读
两个项目的开发环境可以设置在不同的存储位置或文件夹中;无需将所有项目的源码都放置于GOPATH/src目录中。采用这种打包管理方案显著简化了对不同项目的代码管理流程;使得识别当前项目所依赖的软件包变得异常直接;从而便于团队对代码进行审查、转移以及部署工作。
2. Go Modules 应用实践
2.1 Go modules关键信息
- Go命令行工具:创建项目并管理其软件包
- Go配置文件:记录项目的软件包及其版本信息
- Go汇总文件:记录软件包及其版本详细信息的文档
2.1.1 go mod 命令行
go mod init:初始化一个新的模块,并生成 go.mod 文件。该命令需要提供一个项目模块名称作为参数。go mod tidy:整理现有的依赖,将缺失的依赖项添加到 go.mod 文件中,并删除不再需要的模块。go mod graph:查看现有的依赖结构,生成项目所有依赖的报告。该命令会以文本模式打印模块需求图。go mod edit:编辑 go.mod 文件。该命令提供了一些选项,例如 -json、-require 和 -exclude,可以根据需要进行使用。go mod vendor:将项目所有的依赖复制到项目下的 vendor 目录中。这个命令会从 go.mod 文件中获取依赖信息,并将对应的模块复制到 vendor 目录下。go mod download:下载 go.mod 文件中指明的所有依赖。该命令会将所需的模块下载到本地,并按照指定的格式进行存储。go mod verify:验证依赖是否正确。该命令会检查当前模块的依赖关系是否与 go.mod 文件中的记录匹配,并报告任何不匹配的情况。
2.1.2 配置代理服务
某些项目的依赖库位于私有仓库内,并且由于外部防火墙的存在无法访问外部仓库时,则需要通过代理服务器进行下载操作。具体步骤包括:首先在环境中添加GOPROXY变量并赋予相应值;其次切换 download source至可用网络;最后将 download source设为自定义模块路径即可完成任务。详细的命令如下:
go env -w GOPROXY=https://goproxy.cn,direct
代码解读
2.2 创建项目
go mod init demo
代码解读
运行上述命令后将导致在当前目录下创建一个名为 demo 的包 在目录下会生成go.mod文件 go.mod文件的信息如下 它记录了项目的基本信息 列出了项目依赖项列表并定义了项目的元数据
module demo
go 1.21.4
代码解读
module:表示包名为 demo,go 1.21.4:表示 Golang 的版本为1.21.4
2.3 获取依赖包
go get -u github.com/gin-gonic/gin
代码解读
为了解决项目中的问题 我们决定采用 Gin 框架 通过执行 go get 命令行脚本 我们可以轻松获取所需的 Gin 依赖项 下载完成后 将自动生成一个去 sum 文件 同时 在去 mod 文件中会记录这些修改内容
module demo
go 1.21.4
require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
golang.org/x/arch v0.6.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
golang.org/x/net v0.0.0-20210312094802-d4f7f1b4abdc
golang.org/x/text v0.3.0-20210130214432-1127a985f6f5
)
replace (
golang.org/x/net v0.0.0-20210312094802-d4f7f1b4abdc => golang.org/x/net v1.2.3-456789-abcdef
golang.org/x/text v0.3.0-20210130214432-1127a985f6f5 => golang.org/x/text v0.4.0-7890abcdef
)
exclude {
google.golang.org/protobuf v1.31.0
}
代码解读
指定我们即将开发的这个 demo 包所依赖的具体格式信息。具体格式表现为:首字母全部大写的包名称加空格后跟相应的版本号(例如:npm@1.x.x)。其中注释行// indirect表示该引入的包会被当前项目间接依赖。过滤掉的是那些与构建过程相关的特定固定版本依赖项。这意味着在构建过程中不会尝试下载或安装这些特定版本的依赖模块。替代的是某个无法正常获取到可用版本第三方模块的具体路径及版本号信息。当某个目标模块无法通过正常的渠道获取到可用版本时,则可以选择另一个已知可用且易于获取到对应 versions 的替代方案来完成功能需求。
go.sum 文件主要用于记录项目依赖关系和版本信息。该文件无需人工干预地进行修改。它由 Go Modules 管理其更新过程。go.sum 常见内容如下:
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
代码解读
通过完成Go Modules项目的模块化管理后, 使用 go get 命令安装成功的依赖包会被下载至GOPATH/pkg/mod目录下.
2.4 运行项目
创建main函数,main函数所在的包名称必须是 main。例如:
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
代码解读
在main函数所在的文件中指定的package必须指定为main。否则,在启动程序时会提示:package command-line-arguments不是主要包吗?
go run main.go
代码解读
