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

如上图所示,我们可以正常访问咱们自定义的项目啦~
目录
相关文章
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
3天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
11 2
|
3天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
14 2
|
7天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
83 58
|
6天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
3天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
10 4
|
3天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
14 1
|
7天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
7天前
|
Go 数据处理 调度
探索Go语言的并发模型:Goroutines与Channels的协同工作
在现代编程语言中,Go语言以其独特的并发模型脱颖而出。本文将深入探讨Go语言中的Goroutines和Channels,这两种机制如何协同工作以实现高效的并发处理。我们将通过实际代码示例,展示如何在Go程序中创建和管理Goroutines,以及如何使用Channels进行Goroutines之间的通信。此外,本文还将讨论在使用这些并发工具时可能遇到的常见问题及其解决方案,旨在为Go语言开发者提供一个全面的并发编程指南。
|
5天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。