6

Golang 工程管理与业务实践

 2 years ago
source link: https://wenzhiquan.github.io/2021/05/16/2021-05-16-golang-dependency/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

范峥老师从 Golang 的依赖管理的历史和演进过程讲起,详细的阐述了 go mod 的工作原理并讲述了一些常用工具和方法,紧接着,通过几个 case 让我们直观的了解到实际工作中可能遇到的问题及其解决方案。

Golang 工程和依赖管理基本机制

Golang 管理机制的演进

第一代:GOPATH

  • 所有依赖库按路径组织在 $GOPATH/src 下
  • 编译时直接使用 $GOPATH/src 下的代码
  • go get 会下载代码到 $GOPATH/src 下
  • 依赖库的版本控制很麻烦,需要通过控制依赖库代码下载做版本控制
  • 不同环境下依赖库版本不一致

第二代:govendor

  • 所有依赖库按路径组织在项目的 vendor 目录下
  • 编译时使用 vendor 中的代码
  • 通过 govendor 工具来更新 vendor 下的代码虽然 govendor 通过 vendor 目录解决了不同环境下依赖库版本不一致的问题,但是依然没有解决依赖库版本控制麻烦的问题。

第三代:go mod

  • 所有依赖代码按路径和版本号组织在$GOPATH/pkg/mod 目录下
  • 采用 go.mod 文件描述依赖项的版本
  • 通过 go get/mod 等命令管理依赖

通过 go.mod 文件,我们可以轻易的描述出依赖库的版本,并且编译时会使用 mod 目录下的代码,此目录的代码是根据 go.mod 文件描述进行下载的,保证了不同环境依赖库的一致性

go mod 工作原理

go.mod 的版本有两种表达方式:

  1. semver 语义化表达:
  • 每个语义版本都采用v 主版本号。次版本号。修订版本号
  • v 所有版本号都以v开头
  • 主版本号 如果主版本号更新,将意味着 API 版本不再向下兼容
  • 次版本号 增加了新特性,并且向下兼容
  • 修订版本号 向下兼容的 bug 修复
  1. 基于 commit 的伪版本号表达:
  • 每个伪版本号都采用v 基本版本-commit UTC 时间-commit hash 前 12 位
  • v 所有版本号都以v开头
  • commit UTC 时间 时间使用 yyyymmddhhmmss 格式
  • commit hash commit hash 使用前 12 位

使用 go mod 的前提是已经初始化了项目,在项目根目录下使用go mod init即可完成初始化。

有了模块定义,然后执行 go mod tidy 会自动生成依赖,填充 go.mod, go.sum 文件

之后我们可以使用 go list -m all 查看当前项目正在使用的 package 版本,然后执行 go get xxx/xxx 来更新指定的 package

版本选择算法

实际使用中存在的问题

实际使用过程中,由于不是所有的库都是使用 go mod 进行版本控制,我们的 go.mod 中可能会有如上图的两个标记:

  1. // indirect
    表示非本项目所直接依赖,但在本项目中指定该依赖项的版本,可能原因:
  • 依赖项没有使用 go mod
  • 不是所有依赖项都在 go.mod 中
  • 手动为依赖的依赖指定较新的版本
  1. +incompatible
    表示该依赖未使用 go mod 管理依赖
  • 不影响使用,但该依赖的依赖的版本没有被显式指定
  • 不再认为不同 major 版本号为不同项目

工程和依赖管理常见问题

case 1 执行 go get –u 后项目编译不通过

case 2 为什么一些工程不使用 go mod?

case 3 部分项目不使用 go mod 导致的复杂场景

最终参与编译的 X 是 v1 还是 v2?

case 4 惨遭删 tag/branch/commit

case 5 循环依赖陷阱

循环依赖产生的原因是两个 package 之间不能互相 import,但两个不同工程的不同 package 之间可以互相 import

循环依赖一旦形成,内部所有依赖的所有版本都会一直保留,一旦其中一个进行了修改,就很容易出现问题

经验:公共库之间应明确分工,避免大杂烩和循环依赖


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK