1月30日Day8 Go 语言依赖管理|青训营笔记

110 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天

Go 语言依赖管理

Go 语言依赖管理简介

依赖指各种开发包,我们在开发项目中经常利用已经封装好的、经过验证的开发组件或工具来进行开发。 实际工程会相对复杂,我们不可能基于标准库 0~1 编码搭建,而更多的关注业务逻辑的实现,而其他的涉及框架、日志、driver、以及 collection 等一系列依赖都会通过 SDK 的方式引入这样对依赖包的管理就尤为重要。

本文主要对 Go 的依赖管理的演进路线和 Go Module 实践进行简要的概述和记录。

Go 依赖管理演进

Go 的依赖管理主要经历了3个阶段,分别是GOPATH、Go Vendor,到目前被广泛应用的 Go Module。

截屏2023-01-30 16.52.17.png

GOPATH

GOPATH 是 Go 语言支持的一个环境变量,value 是 Go 项目的工作区。
目录有以下结构:

  • src:存放 Go 项目的源码
    • 项目代码直接依赖 src 下的代码
    • go get 下载最新版本的包到1 src 目录下
  • pkg:存放编译的中间产物,加快编译速度
  • bin:存放 Go 项目编译生成的二进制文件

弊端

截屏2023-01-30 16.52.35.png

同一个pkg,有2个版本,A->A(),B->B(),而 src 下只能有一个版本存在,那 AB 项目无法保证都能编译通过。也就是在 GOPATH 管理模式下,如果多个项目依赖同一个库,则依赖该库是同一份代码,所以不同项目不能依赖同一个库的不同版本。

Go Vendor

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

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

·
| - README.md
| - dao
| - handler
| - main.go
| - service
| - vendor

弊端

截屏2023-01-30 16.52.54.png

项目 A 依赖 pkg b 和 c,而 B 和 C 依赖了 D 的不同版本,通过vendor的管理模式我们不能很好的控制对于 D 的依赖版本。归根到底vendor不能很清晰的标识依赖的版本概念。

Go Module

  • 通过 go.mod 文件管理依赖包版本
  • 通过 go get/go mod 指令工具管理依赖包

依赖管理三要素

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

依赖配置 go.mod

截屏2023-01-30 17.37.39.png

  • 模块路径用来标识一个模块,从模块路径可以看出从哪里找到该模块,如果是 github 前缀则表示可以从 Github 仓库找到该模块,依赖包的源代码由 github 托管,如果项目的子包想被单独引用,则需要通过单独的init go.mod文件进行管理。
  • 下面是依赖的原生sdk版本
  • 最下面是单元依赖,每个依赖单元用模块路径+版本来唯一标示。

依赖配置 version

go mod为了方便管理则定义了版本规则,分为语义化版本和基于 commit 伪版本;

  • 语义化版本不同的 MAJOR 版本表示是不兼容的 API,所以即使是同一个库,MAJOR 版本不同也会被认为是不同的模块;MINOR 版本通常是新增函数或功能,向后兼容; 而 patch 版本一般是修复bug;
  • 基于commit的伪版本基础版本前缀是和语义化版本一样的;时间戳(vvvvmmddhhmmss),也就是提交 Commit 的时间,最后是校验码 (abcdefabcdef),包含 12 位的哈希前缀;每次提交 commit 后 Go 都会默认生成一个伪版本号。
// 语义化版本
// ${MAJOR}.${MINOR}.${PATCH}
V1.3.0
V2.3.0
// 基于 commit 伪版本
// vx.0.0-yyyymmddhhmmss-abcdefgh1234
v0.0.0-20220401081311-c3865932667
v1.0.0-20201130134442-10cb98267c6c

注:

  • indirect后缀,表示go.mod对应的当前模块,没有直接导入该依赖模块的包,也就是非直接依赖,表示间接依赖.
  • incompatible后缀,主版本 2+ 模块会在模块路径增加/vN后缀,这能让 go module 按照不同的模块来处理同一个项目不同主版本的依赖。

依赖分发 Proxy

github是比较常见给的代码托管系统平台,而 Go Modules 系统中定义的依赖,最终可以对应到多版本代码管理系统中某一项目的特定提交或版本,这样的话,对于 go.mod 中定义的依赖,则直接可以从对应仓库中下载指定软件依赖,从而完成依赖分发。

但直接使用版本管理仓库下载依赖,存在多个问题:

  • 首先无法保证构建确定性:软件作者可以直接代码平台增加/修改/删除软件版本,导致下次构建使用另外版本的依赖,或者找不到依赖版本。
  • 无法保证依赖可用性:依赖软件作者可以直接代码平台删除软件,导致依赖不可用;
  • 大幅增加第三方代码托管平台压力。

截屏2023-01-30 16.55.04.png

Go Proxy 是解决这些问题的方案,Go Proxy 是一个服务站点,它会缓源站中的软件内容,缓存的软件版本不会改变,并且在源站软件删除之后依然可用,从而实现了"immutability"和"available"的依赖分发;
使用 Go Proxy之后,构建时会直接从 Go Proxy 站点拉取依赖。

Go Proxy 的使用

Go Modules 通过 GOPROXY 环境变量控制如何使用 Go Proxy;GOPROXY 是一个 Go Proxy 站点URL列表,可以使用 "direct" 表示源站。对于示例配置,整体的依赖寻址路径,会优先从 proxy1 下载依赖,如果 proxy1 不存在会到 proxy2 寻找,如果 proxy2 中不存在则会回源到源站直接下载依赖,缓存到 proxy 站点中。

go get

IMG_5369.jpg

go mod

IMG_5370.jpg