Golang语言基于go module方式管理包(package)

简介: 这篇文章详细介绍了Golang语言中基于go module方式管理包(package)的方法,包括Go Modules的发展历史、go module的介绍、常用命令和操作步骤,并通过代码示例展示了如何初始化项目、引入第三方包、组织代码结构以及运行测试。

                                              作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.Go Modules发展史

1.前言

一般编程语言都会提供依赖库管理工具,例如python的pip、node.js的npm,java的maven,rust的cargo,Go语言也有提供自己的依赖库管理工具。

Go语言在1.11提出了Go mod,每次版本或多或少都会对go.mod进行改进优化,go mod也越来越好,当前大多数公司都使用go mod来管理依赖库。

考虑新手很容易在"go module"使用上犯迷糊,本篇文章主要介绍"Go Modules"发展史及快速入门的必要知识点。

2.早期第三方包存储在GOPATH路径

起初Go语言在1.5之前没有依赖管理工具,若想引入依赖库,需要执行go get命令将代码拉取放入GOPATH/src目录下。

这样的依赖管理方式存在一个致命的缺陷,那就是不支持版本管理,同一个依赖包只能存在一个版本的代码。

可是我们本地的多个项目完全可能分别依赖同一个第三方包的不同版本。

3.vendor阶段

为了解决隔离项目的包依赖问题,Go1.5版本推出了vendor机制,环境变量中有一个GO15VENDOREXPERIMENT需要设置为1,该环境变量在Go1.6版本时变成默认开启,目前已经退出了历史舞台;

vendor其实就是将原来放在"GOPATH/src"的依赖包放到工程的vendor目录中进行管理,不同工程独立地管理自己的依赖包,相互之间互不影响,原来是包共享的模式,通过vendor这种机制进行隔离,在项目编译的时候会先去vendor目录查找依赖,如果没有找到才会再去GOPATH目录下查找。

vendor阶段的优劣势:
    优点:
        保证了功能项目的完整性,减少了下载依赖包,直接使用vendor就可以编译

    缺点:
        仍然没有解决版本控制问题,go get仍然是拉取最新版本代码。

4.社区管理工具层出不穷

很多优秀的开发者在这期间也都实现了不错的包依赖管理工具,例如:
  godep:
    https://github.com/tools/godep

  govendor:
    https://github.com/kardianos/govendor

  glide:
    https://github.com/Masterminds/glide

  dep:
    https://github.com/golang/dep

dep应该是其中最成功的,得到了Go语言官方的支持,该项目也被放到了https://github.com/golang/dep,但是为什么dep没有称为官宣的依赖工具呢?

其实因为随着"Russ Cox"与"Go团队"中的其他成员不断深入地讨论,发现dep的一些细节似乎越来越不适合Go,因此官方采取了另起"proposal(建议)"的方式来推进,其方案的结果一开始先是释出”vgo“,最终演变为我们现在所见到的”Go modules“;

5.go modules官宣官方管理工具

go modules是"Russ Cox"(Go语言核心开发团队人员之一)推出来的,发布于Go1.11。

go modules成长于Go1.12,丰富于Go1.13,正式于Go1.14推荐在生产上使用。

go modules几乎后续的每个版本都或多或少的有一些优化,比如在Go1.16引入go mod retract、在Go1.18引入go work工作区的概念。

参考连接:
    https://developer.aliyun.com/article/1604714

二.go module介绍

1.GO111MODULE环境变量

这个环境变量是"Go Modules"的开关,主要有以下参数:
    auto:
        只在项目包含了go.mod文件时启动go modules,在Go1.13版本中是默认值。
    on:
        无脑启动Go Modules,推荐设置,Go1.14版本以后的默认值。
    off:
        禁用Go Modules,一般没有使用go modules的工程使用。

我现在使用的Go版本是1.22.4,默认GO111MODULE=on。

2.GOPROXY

这个环境变量主要是用于设置Go模块代理"Go module proxy",其作用是用于使Go在后续拉取模块版本时能够脱离传统的VCS方式,直接通过镜像站点来快速拉取。

GOPROXY的默认值是"https://proxy.golang.org,direct",由于某些原因国内无法正常访问该地址,所以我们通常需要配置一个可访问的地址。目前社区使用比较多的有两个"https://goproxy.cn"和"https://goproxy.io",当然如果你的公司有提供GOPROXY地址那么就直接使用。

设置GOPAROXY的命令如下:
    # go env -w GOPROXY=https://goproxy.cn,direct

GOPROXY允许设置多个代理地址,多个地址之间需使用英文逗号","分隔。最后的"direct"是一个特殊指示符,用于指示Go回源到源地址去抓取(比如"GitHub"等)。

当配置有多个代理地址时,如果第一个代理地址返回"404"或"410"错误时,Go会自动尝试下一个代理地址,当遇见"direct"时触发回源,也就是回到源地址去抓取。

3.GOSUMDB

该环境变量的值是一个Go checksum database,用于保证Go在拉取模块版本时拉取到的模块版本数据未经篡改。

若发现不一致会中止,也可以将值设置为off即可以禁止Go在后续操作中校验模块版本;

什么是Go checksum database?

Go checksum database主要用于保护Go不会从任何拉到被篡改过的非法Go模块版本,详细算法机制可以看一下:
    https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md#proxying-a-checksum-database

GOSUMDB的默认值是sum.golang.org,默认值与自定义值的格式不一样,默认值在国内是无法访问,这个值我们一般不用动,因为我们一般已经设置好了GOPROXY,goproxy.cn支持代理sum.golang.org;

GOSUMDB的值自定义格式如下:
    - 格式1:
        <SUMDB_NAME>+<PUBLIC_KEY>。
    - 格式2:
        <SUMDB_NAME>+<PUBLIC_KEY> <SUMDB_URL>。

4.GONOPROXY/GONOSUMDB/GOPRIVATE

这三个环境变量放在一起说,一般在项目中不经常使用,这三个环境变量主要用于私有模块的拉取。

在GOPROXY、GOSUMDB中无法访问到模块的场景中,例如拉取git上的私有仓库。

GONOPROXY、GONOSUMDB的默认值是GOPRIVATE的值,所以我们一般直接使用GOPRIVATE即可,其值也是可以设置多个,以英文逗号进行分割。

"GOPRIVATE"用来告诉"go"命令哪些仓库属于私有仓库,不必通过代理服务器拉取和校验。

"GOPRIVATE"的值也可以设置多个,多个地址之间使用英文逗号","分隔。我们通常会把自己公司内部的代码仓库设置到"GOPRIVATE"中,例如:
    $ go env -w GOPRIVATE="git.yinzhengjie.com" 

这样在拉取以"git.yinzhengjie.com"为路径前缀的依赖包时就能正常拉取了。

此外,如果公司内部自建了"GOPROXY"服务,那么我们可以通过设置"GONOPROXY=none",允许从内部代理拉取私有仓库的包。

也可以使用通配符的方式进行设置,对域名设置通配符号,这样子域名就都不经过Go module proxy和Go checksum database;

5.go.mod文件

go.mod是启用Go modules的项目所必须且最重要的文件,其描述了当前项目的元信息,每个go.mod文件开头符合包含如下信息:
    module:
        用于定义当前项目的模块路径(突破$GOPATH路径)。

    go:
        当前项目Go版本,目前只是标识作用、

    require:
        用设置一个特定的模块版本,可以指定需要依赖的版本号。
        Go modules中建议使用语义化版本控制,其建议的版本号格式如下:
            主版本号:
                发布了不兼容的版本迭代时递增(breaking changes)。
            次版本号:
                发布了功能性更新时递增。
            修订号:
                发布了bug修复类更新时递增。

    exclude:
        用于从使用中排除一个特定的模块版本。

    replace:
        用于将一个模块版本替换为另外一个模块版本。 
        比如将一个无法访问的域名替换成国内的镜像站点或者本地路径均可。

    retract:
        用来声明该第三方模块的某些发行版本不能被其他模块使用,在Go1.16引入。
        如果某个发布的版本存在致命缺陷不再想让用户使用时,我们可以使用retract声明废弃的版本。

    indirect:
        行尾的indirect表示该依赖包为间接依赖,说明在当前程序中的所有"import"语句中没有发现引入这个包。

6.go.sum文件

使用go module下载了依赖后,项目目录下还会生成一个go.sum文件。

这个文件中详细记录了当前项目中引入的依赖包的信息及其hash 值。go.sum文件内容通常是以类似下面的格式出现。
    - <module> <version>/go.mod <hash>
    - <module> <version> <hash>
    - <module> <version>/go.mod <hash>

不同于其他语言提供的基于中心的包管理机制,例如“npm”和“pypi”等,Go并没有提供一个中央仓库来管理所有依赖包,而是采用分布式的方式来管理包。为了防止依赖包被非法篡改,Go module引入了go.sum机制来对依赖包进行校验。

7."go mod"依赖保存位置

"go mod download"会将依赖缓存到本地,Go module缓存的目录是"$GOPATH/pkg/mod/cache“、"GOPATH/pkg/sum"。

这些缓存依赖可以被多个项目使用,未来可能会迁移到$GOCACHE下面。

每个依赖包都会带有版本号进行区分,这样就允许在本地存在同一个包的多个不同版本。


温馨提示:
    如果想清除所有本地已缓存的依赖包数据,可以执行"go clean -modcache"命令。

三. go module常用命令

1."go mod"常用的子命令概述

命令 介绍
go mod init 初始化项目依赖,生成go.mod文件
go mod download 根据go.mod文件下载依赖
go mod tidy 比对项目文件中引入的依赖与go.mod进行比对
go mod graph 输出依赖关系图
go mod edit 编辑go.mod文件
go mod vendor 将项目的所有依赖导出至vendor目录
go mod verify 检验一个依赖包是否被篡改过
go mod why 解释为什么需要某个依赖
Go module 是 Go1.11 版本发布的依赖管理方案,从 Go1.14 版本开始推荐在生产环境使用,于Go1.16版本默认开启。

如上表所示,Go module 提供了以上命令供我们使用,我们可以使用“go help mod”查看可以使用的命令。

Go语言在"go module"的过渡阶段提供了"GO111MODULE"这个环境变量来作为是否启用"go module"功能的开关。

考虑到Go1.16之后 "go module"已经默认开启,所以本书不再介绍该配置,对于刚接触Go语言的读者而言完全没有必要了解这个历史包袱。

2.使用go module引入包第三方包

2.1 初始化项目

    1.初始化项目
yinzhengjie@bogon 02-crm % pwd
/Users/yinzhengjie/golang/gosubjects/src/gocode/devops/05-package/02-crm
yinzhengjie@bogon 02-crm % 
yinzhengjie@bogon 02-crm % ls -l
total 0
yinzhengjie@bogon 02-crm % 
yinzhengjie@bogon 02-crm % go mod init web-server
go: creating new go.mod: module web-server
yinzhengjie@bogon 02-crm % 
yinzhengjie@bogon 02-crm % ls -l                 
total 8
-rw-r--r--@ 1 yinzhengjie  staff  29  7 14 23:00 go.mod
yinzhengjie@bogon 02-crm % 
yinzhengjie@bogon 02-crm % cat go.mod 
module web-server

go 1.22.4
yinzhengjie@bogon 02-crm % 


    2.go.mod内容说明:
module web-server:
    定义当前项目的导入路径。

go 1.22.4:
    标识当前项目使用的go版本。


温馨提示:
    go.mod文件会记录项目使用的第三方依赖包信息,包括包名和版本,由于我们的"web-server"项目目前还没有使用到第三方依赖包,所以go.mod文件暂时还没有记录任何依赖包信息,只有当前项目的一些信息。

2.2 下载第三方程序包

    1.安装gin框架
yinzhengjie@bogon 02-crm % go get -u github.com/gin-gonic/gin


    2.查看本地
yinzhengjie@bogon 02-crm % ls -l
total 24
-rw-r--r--@ 1 yinzhengjie  staff  1376  7 14 23:17 go.mod  # 依赖文件
-rw-r--r--@ 1 yinzhengjie  staff  7028  7 14 23:17 go.sum  # 依赖包的校验文件
yinzhengjie@bogon 02-crm % 
yinzhengjie@bogon 02-crm % cat go.mod  # 关于go.mod的文件内容解析请参考上文中的介绍哟~
module web-server

go 1.22.4

require (
        github.com/bytedance/sonic v1.11.9 // indirect
        github.com/bytedance/sonic/loader v0.1.1 // indirect
        github.com/cloudwego/base64x v0.1.4 // indirect
        github.com/cloudwego/iasm v0.2.0 // indirect
        github.com/gabriel-vasile/mimetype v1.4.4 // indirect
        github.com/gin-contrib/sse v0.1.0 // indirect
        github.com/gin-gonic/gin v1.10.0 // indirect
        github.com/go-playground/locales v0.14.1 // indirect
        github.com/go-playground/universal-translator v0.18.1 // indirect
        github.com/go-playground/validator/v10 v10.22.0 // indirect
        github.com/goccy/go-json v0.10.3 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
        github.com/klauspost/cpuid/v2 v2.2.8 // indirect
        github.com/leodido/go-urn v1.4.0 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/pelletier/go-toml/v2 v2.2.2 // indirect
        github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
        github.com/ugorji/go/codec v1.2.12 // indirect
        golang.org/x/arch v0.8.0 // indirect
        golang.org/x/crypto v0.25.0 // indirect
        golang.org/x/net v0.27.0 // indirect
        golang.org/x/sys v0.22.0 // indirect
        golang.org/x/text v0.16.0 // indirect
        google.golang.org/protobuf v1.34.2 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
)
yinzhengjie@bogon 02-crm %  



温馨提示:
    1.在执行go get命令下载一个新的依赖包时一般会额外添加"-u"参数,强制更新现有依赖;
    2.行尾的indirect表示该依赖包为间接依赖,说明在当前程序中的所有"import"语句中没有发现引入这个包;
    3.下载软件包的名称和版本使用"@"区分,若不指定版本则默认会下载最新的发布版本;

3.代码测试

3.1 项目组织结构

如上图所示,是我随意找的目录,做的目录组织结构,其中包括引入第三方包和本项目中自定义包引入。

3.2 blog.go源代码

package blog

import (
    // 导入第三方仓库代码
    "github.com/gin-gonic/gin"
)

func StartWebServer() {
    // 创建一个默认的路由引擎
    r := gin.Default()

    // GET:请求方式;/hello:请求的路径
    // 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
    r.GET("/hello", func(c *gin.Context) {
        // c.JSON:返回JSON格式的数据
        c.JSON(200, gin.H{
            "name": "尹正杰",
            "blog": "https://www.cnblogs.com/yinzhengjie",
        })
    })

    // 启动HTTP服务,默认在0.0.0.0:8080启动服务
    r.Run()
}

3.3 main.go源代码

package main

import (
    // 注意,此处的"web-server"是"go.mod文件中记录的"module web-server"对应的名称哟~
    // 而"web-server"下对应的"blog"是我们自定义的包名哟~
    "web-server/blog"
)

func main() {
    blog.StartWebServer()
}

3.4 运行项目并访问WebUI

如上图所示,我们可以正常访问咱们自定义的项目啦~
目录
相关文章
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
140 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
112 3
Golang语言之gRPC程序设计示例
|
11天前
|
Linux Go iOS开发
怎么禁用 vscode 中点击 go 包名时自动打开浏览器跳转到 pkg.go.dev
本文介绍了如何在 VSCode 中禁用点击 Go 包名时自动打开浏览器跳转到 pkg.go.dev 的功能。通过将 gopls 的 `ui.navigation.importShortcut` 设置为 &quot;Definition&quot;,可以实现仅跳转到定义处而不打开链接。具体操作步骤包括:打开设置、搜索 gopls、编辑 settings.json 文件并保存更改,最后重启 VSCode 使设置生效。
36 7
怎么禁用 vscode 中点击 go 包名时自动打开浏览器跳转到 pkg.go.dev
|
26天前
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
36 4
|
25天前
|
Go 索引
go语言使用strings包
go语言使用strings包
23 3
|
1月前
|
编译器 Go 开发者
go语言中导入相关包
【11月更文挑战第1天】
32 3
|
1月前
|
存储 Cloud Native Shell
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
|
1月前
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
|
3月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
75 3
Golang语言之Prometheus的日志模块使用案例
|
2月前
|
存储 Go 数据库
Go语言Context包源码学习
【10月更文挑战第21天】Go 语言中的 `context` 包用于在函数调用链中传递请求上下文信息,支持请求的取消、超时和截止时间管理。其核心接口 `Context` 定义了 `Deadline`、`Done`、`Err` 和 `Value` 方法,分别用于处理截止时间、取消信号、错误信息和键值对数据。包内提供了 `emptyCtx`、`cancelCtx`、`timerCtx` 和 `valueCtx` 四种实现类型,满足不同场景需求。示例代码展示了如何使用带有超时功能的上下文进行任务管理和取消。