Golang 基础 Go Modules包管理

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  world

hello 目录下有一个 go.mod 文件,说明了这是一个模块,该模块包含了 hello 包和一个子包 world。该目录中也包含了一个 go.sum 文件,该文件供 Go 命令在构建时判断依赖包是否合法。

Go Modules 命令

Go Modules 的管理命令为go mod,go 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 Modules,Go1.14 版本推荐打开,未来版本会设为默认值。
  • off:关闭 Go Modules,不推荐。

所以,如果要打开 Go Modules,建议直接设置export GO111MODULE=on。

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.0+incompatiblegolang.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:

  1. 在项目开发初期,A 项目依赖 B 项目的包,但 B 项目因为种种原因没有 push 到仓库,这时也可以在 go.mod 中把依赖包替换为 B 项目的本地磁盘路径。

  2. 在国内访问 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/v3),go 会在 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/673697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea2023创建spring项目无法选择Java8

idea2023创建spring项目无法选择Java8 今天下载了新版的idea 2023.3.2&#xff0c;但是在创建springboot项目的时候只能选择Java17和Java21&#xff0c;没法选择其他的版本。 使用下面阿里云的地址替换Server URL中的start.spring.io的地址即可 https://start.aliyun.com/替…

C#调用WechatOCR.exe实现本地OCR文字识别

最近遇到一个需求&#xff1a;有大量的扫描件需要还原为可编辑的文本&#xff0c;很显然需要用到图片OCR识别为文字技术。本来以为这个技术很普遍的&#xff0c;结果用了几个开源库&#xff0c;效果不理想。后来&#xff0c;用了取巧的方法&#xff0c;直接使用了WX的OCR识别模…

Linux大集合

Linux Linux是什么&#xff1f; Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户、多任务、 支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和 64位硬件。 Linux内核 是一个Linux系统…

可达鸭二月月赛——入门赛第四场T3题解

姓名 王胤皓 AC 记录 题目&#xff1a; 思路 用数组进行操作太难&#xff0c;而这些操作可以再 STL 中的 vector 容器&#xff0c;有 insert 和 erase 函数&#xff0c;所以非常方便。 vector 下标从 0 0 0 开始&#xff0c;所以所有操作都要 − 1 -1 −1。 操作 1 1 1 …

Redis的数据类型Hash使用场景实战

Redis的数据类型Hash使用场景 常见面试题&#xff1a;redis在你们项目中是怎么用的&#xff0c;除了String数据类型还使用什么数据类型&#xff1f; 怎么保证缓存和数据一致性等问题… Hash模型使用场景 知识回顾&#xff1a; redisTemplate.opsForHash() 方法是 Redis 的 …

Spring Boot3整合Redis

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 前置条件 1.导依赖 2.配置连接信息以及连接池参数 3.配置序列化方式 4.编写测试 前置条件 已经初始化好一个spr…

MyBatisPlus基础操作之增删改查

目录 一、基本使用 1.1 插入数据 1.2 删除操作 1.3 更新操作 二、条件构造器Wrapper 2.1 常用AbstractWrapper方法 2.1.1 示例一 2.2.2 示例二 2.2.3 示例三 2.2 常用QueryWrapper方法 2.2.1 示例一 2.2.2 示例二 2.2.3 示例三&#xff08;常用&#xff09; 2.3 常…

1978-2022年地级市全要素生产率数据

1978-2022年地级市全要素生产率数据 1、时间&#xff1a;1978-2022年 2、来源&#xff1a;城市统计年鉴以及各省市的统计年鉴 3、指标&#xff1a;省份、地区、年份、OLS、FE、RE、DGMM、SGMM、SFA1、SFA2、SFA3、SFA3D、TFE、非参数法 4、范围&#xff1a;421地区 5、参考…

Deepin基本环境查看(八)【系统安全:房、车、查房、查车】

Deepin基本环境查看&#xff08;八&#xff09;【系统安全&#xff1a;房、车、查房、查车】 - 相关文章目录1、概述2、想象中的... 现实中的...1&#xff09;想象中的我2&#xff09;梦幻中的我3&#xff09;现实中的我 3 要房、要车、还是房车都要1&#xff09;超级计算机2&a…

Pymysql之Connection中常用API

Connection中常用API 1、open() &#xff1a;检测数据库是否连接。 connect.open&#xff1a;如果数据库连接返回Trhe&#xff0c;否则返回False。 2、ping(reconnectTrue) connect.ping(reconnectTrue):如果reconnectTrue表示连接断开后&#xff0c;重新进行连接。 import…

Docker-Learn(三)创建镜像Docker(换源)

根据之前的内容基础&#xff0c;本小点的内容主要涉及到的内容是比较重要的文本Dockerfile 1. 编辑Dockerfile 启动命令行终端&#xff08;在自己的工作空间当中&#xff09;,创建和编辑Dockerfile。 vim Dockerfile然后写入以下内容 # 使用一个基础镜像 FROM ubuntu:late…

Centos8保姆级安装教程

1.下载地址 Downloadhttps://www.centos.org/download/ 2.安装教程 第一步创建新的虚拟机 第二步自定义高级 第三步这里是选择系统的兼容性&#xff0c;默认就可以 之后直接下一步 第四步选择稍后安装操作系统 之后点击下一步 第五步选择操作系统Linux 安装的是centos8 64位…

Ps:信息面板选项

点击“信息”面板控制菜单按钮&#xff0c;打开“信息面板选项” Info Panel Options对话框。 第一颜色信息 First Color Readout 指定在“信息”面板中的“第一颜色信息”区域显示的像素颜色值所基于的颜色模式。 模式 Mode --实际颜色 Actual Color 默认选项。显示在当前颜色…

第二十九天| 491.递增子序列 、46.全排列、47.全排列 II

Leetcode 491.递增子序列 题目链接&#xff1a;491 递增子序列 题干&#xff1a;给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出…

服装设计公司,如何用钉钉实现企业数字化成功转型?

钉钉作为数字化工作平台&#xff0c;为某服装设计公司实现了组织管理的数字化转型&#xff0c;构建了一站式的工作平台。通过钉钉赋能&#xff0c;有利于企业推进组织架构、员工沟通、产品运营和客户服务等方面的数字化、智能化转型。 借助钉钉平台&#xff0c;该服设公司轻松实…

oracle主库增加redo组数

redo log&#xff08;重做日志&#xff09;&#xff1a; 重做日志&#xff1a;简单来说就是&#xff0c;将oracle数据库的DML、DDL&#xff08;数据库操作语言&#xff0c;数据库定义i语言&#xff09;操作记录在日志中&#xff0c;方便恢复及备库使用&#xff0c;以组的方式管…

PCIE Order Set

1 Training Sequence Training Sequence是由Order Set(OS) 组成&#xff0c;它们主要是用于bit aligment&#xff0c;symbol aligment&#xff0c;交换物理层的参数。当data_rate 2.5GT or 5GT 它们不会被扰码(scramble)&#xff0c;当date_rate 8GT or higher 根据特殊的规…

计算机网络-无线通信技术与原理

一般我们网络工程师接触比较多的是交换机、路由器&#xff0c;很少涉及到WiFi和无线设置&#xff0c;但是呢在实际工作中一般企业也是有这些需求的&#xff0c;这就需要我们对于无线的一些基本配置也要有独立部署能力&#xff0c;今天来简单了解一下。 一、无线网络基础 1.1 无…

ruoyi-nbcio中xxl-job的安装与使用

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a; http://122.227.135.243:9666 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbach…

ChatGPT4 教你如何完成SQL的实践应用

对数据库的各项应用与操作都离不开SQL来对数据进行增删改查。 例如 &#xff1a; 有一张某公司职员信息表如下&#xff1a; 需求1&#xff1a;在公司职员信息表中&#xff0c;请统计各部门&#xff0c;各岗位下的员工人数。 如果这个SQL语句不会写或者不知道怎么操作可以交给…