golang依赖管理 | 青训营

137 阅读4分钟

GOPATH

│
├── bin 项目编译的二进制文件
├── pkg 项目编译的中间产物 加速编译
└── src 项目源码

GOPATH依赖管理弊端

image.png

🤍 场景: A和B依赖于某一个package的不同版本 问题:无法实现package的多版本控制

在gopath管理模式下,如果多个项目依赖同一个库,则依赖该库是同一份代码,所以不同项目不能依赖同一个库的不同版本,这很显然不能满足我们的项目依赖需求。

为了解决这问题,govender出现了。

Go Vendor

项目且录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor 依赖寻址方式:vendor=>GOPATH

│
├── dao 
├── handler
├── main.go
├── service 
└── vendor

通过每个项目引入一份依赖的副本解决了多个项目需要同一个package依赖的冲突问题。

在Vendor机制下,如果当前项目存在Vendor目录,会优先使用该目录下的依赖,如果依赖不存在,会从GOPATH中寻找;这样****。但vendor无法很好解决依赖包的版本变动问题和一个项目依赖同一个包的不同版本的问题,下面我们看一个场景

Go Vendor 弊端

image.png

如图项目A依赖pkg b和c,而B和C依赖了D的不同版本,通过vendor的管理模式我们不能很好的控制对于D的依赖版本,一旦更新项目,有可能带来问题。归根到底vendor不能很清晰的标识依赖的版本概念。下面,go module就应运而生了。

Go Module

通过 go.mod 文件管理依赖包版本 ****

通过 go get/go mod 指令工具管理依赖包

终极目标:定义版本规则和管理项目的依赖关系

依赖管理三要素

  1. 配置文件,描述依赖 go.mod
  2. 中心仓库管理依赖库 proxy
  3. 本地工具 go get/mod

依赖管理 go.mod

module practice  //依赖管理基本单元

go 1.20          //原生库

require(
		example/lib1 v1.0.2  //单元依赖
)

依赖标识: [Module Path][Version/Pseudo-version]

依赖配置 version

语义化版本

MAJOR.{MAJOR}.{MINOR}.${PATCH} V1.3.0 V2.3.0

不同的 MAJOR 版本表示是不兼容的 API,所以即使是同一个库,MAJOR 版本不同也会被认为是不同的模块;

MINOR 版本通常是新增函数或功能,向后兼容;

PATCH 版本一般是修复 bug ;

基于commit伪版本

vX.0.0-yyyymmddhhmmss-abcdefgh1234 v0.0.0-20220401081311-c38fb59326b7

依赖配置 indirect

require(
	example/lib1 v1.0.2
	example/lib2 v1.0.0 //indirect
)
A->B->C    A->B 直接依赖  A->C 间接依赖

依赖配置 incompatible

require(
	example/lib1 v1.0.2+incompatible
)

主版本2+模块会在模块路径增加N后缀。 对于没有go.mod文件并且主版本2+的依赖,会+incompatible

在 Go 模块中,当在 go.mod 文件中的 require 块中指定了 +incompatible 标记时,它表示要求使用不兼容的模块版本。

当使用 +incompatible 标记时,模块管理工具(如 Go Modules)会放宽对模块版本的兼容性要求,允许使用不符合语义化版本规范的模块版本。

通常,Go 模块使用语义化版本(Semantic Versioning,SemVer)规范来指定模块版本号。根据语义化版本规范,版本号由主版本号、次版本号和修订号组成(例如 v1.2.3)。通过增加主版本号表示不兼容的变更。

然而,有时候某些模块可能不遵循语义化版本规范,或者版本号中的主版本号不准确地表示了兼容性的变化。在这种情况下,可以使用 +incompatible 标记来允许使用这些不兼容的模块版本。

需要注意的是,使用 +incompatible 标记会降低代码的可移植性,因为依赖于不兼容的模块版本可能会导致在其他环境或项目中遇到问题。因此,应该谨慎使用 +incompatible 标记,并尽量遵循语义化版本规范。

总结来说, +incompatible 标记用于表示要求使用不符合语义化版本规范的模块版本,在某些情况下可以放宽对模块兼容性的要求,但需要注意降低代码的可移植性。

依赖配置 依赖图

image.png

如果X项目依赖了A、B两个项目,且A、B分别依赖了C项目的vM.3、VM.4两个版本,最终编译时所使用的C项目的版本为如下哪个选项?(单选)

A. V1.3

B. V1.4

C. A用到C时用V1.3编译,B用到c时用V1.4编译

🤍 B 选择最低的兼容版本

依赖分发 回源

image.png

  1. 无法保证构建稳定性 增加/修改/删除软件版本
  2. 无法保证依可用性 删除软件
  3. 增加第三方压力 代码托管平台负载问题

依赖分发 Proxy

image.png

Go Proxy 是一个服务站点,它会缓源站中的软件内容,缓存的软件版本不会改变,并且在源站软件删除之后依然可用,从而实现了供“immutability”和“available”的依赖分发;

使用 Go Proxy 之后,构建时会直接从 Go Proxy 站点拉取依赖。类比项目中,下游无法满足我们上游的需求

依赖分发 变量 GORPOXY

GOPROXY="proxy1.cn,proxy2.cn ,direct" 服务站点URL列表,"direct'”表示源站

🤍 Proxy 1 → Proxy 2 → Direct

工具 go get

go get [example.org/pkg](<http://example.org/pkg>)

  1. @update 默认
  2. @none 删除依赖
  3. @v1.1.2 tag版本 语义版本
  4. @23dfdd5 特定的commit
  5. @master 分支的最新commit

工具 go mod

go mod

  1. init 初始化 创建go.mod文件
  2. download 下载模块到本地缓存
  3. tidy 增加需要的依赖 删除不需要的依赖

管理依赖三要素

  1. 配置文件 描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go get/mod