Go依赖管理 | 青训营

87 阅读5分钟

依赖管理的出现背景

什么是依赖以及为什么需要它

我们在开发一个比较复杂的项目时,如果要从标准库一步一步实现该项目所需要的各种基础功能(比如网络协议功能、网络应用服务功能、数据库操作功能等等)是不太现实的。因为重新开发这些可以复用、可以迁移至不同项目的基础功能不仅会浪费大量的时间和精力,还会由于对这些功能的不太深入的理解而使项目的运行效率达不到预期。
这时,我们就需要引入别人封装的代码,直接调用它们开放的API接口,在不需要深入理解接口的具体底层实现的前提下进一步开发我们项目的其它功能。这样项目的开发效率能够提高,我们也能将精力聚焦于业务逻辑的实现之上。
我们从第三方引入的代码就被称为依赖(dependency),因为我们的项目依赖于这些代码运行。在C、Python等语言中称为库(library),在Go、JS等语言中称为包(package)。
在Go中,使用import关键字引入依赖。
例如:从gorm引入gorm包和sqlite驱动包

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

除此之外,在每个Go文件的开头,我们必须使用package [package name]声明该文件所属的包。
然后我们同样可以使用import [pacakge name]引入我们自己编写的包,并使用其中的接口。这使得我们在开发复杂项目时,可以将项目划分为多个包,每个包负责实现专一的功能,便于对每个功能分别进行测试和维护,从而达到解耦的目的。

但是,当项目使用的依赖越来越多、不同项目使用不同依赖包的版本时、依赖之间的关系趋于复杂时,我们就需要一套高效且易用的方法来管理这些依赖们,否则就会出现依赖失效,项目无法运行的可怕后果。

Go依赖管理方案的发展历史

Go依赖管理方案的核心思想:

  1. 隔离不同项目使用的不同版本的依赖
  2. 控制每个依赖的版本

按照时间顺序,Go分别提出了以下三种方案。

GOPATH

GOPATH是Go中的一个环境变量,它是Go环境下的工作区。GOPATH可以根据需要进行修改。与之相对的是GOROOT,它是Go语言本身的安装目录,存放Go语言本身的工具。
GOPATH的工程结构如下:

  • src:项目源码。
  • pkg:项目中间产物。
  • bin:项目编译完成的二进制文件。

其中,项目直接依赖于存放在src中的代码。
我们可以使用go get 依赖路径下载对应的依赖包至src下供我们的项目调用。

使用GOPATH方案管理依赖存在以下弊端:

  1. 当我们存在多个项目时,需要频繁更改GOPATH的值,否则无法做到隔离不同项目下的依赖。
  2. 无法实现依赖包的多版本控制。

假如我们有两个项目A和B,项目A依赖于包的v1版本、项目B依赖于包的v2版本,而包的v1版本和v2版本不属于同一条分支,那么相同依赖包的版本就会发生冲突,我们就无法同时构建A和B两个项目。

Go Vendor

为了解决GOPATH的缺陷,我们在每个项目下增加vendor文件夹,用于存放当前项目所使用的依赖包的副本,并且当项目寻找依赖时,优先从vendor中寻找,寻找失败再从GOPATH/src中寻找。
为每个项目创建自己的依赖副本,就可以将不同版本的相同依赖隔离开,也就解决了多项目使用相同依赖包的版本冲突问题。
但是Vendor依旧是依赖于项目源码,还是无法控制依赖的版本,它并没有对依赖的版本进行规定和管理。于是,当我们更新项目时仍会出现依赖冲突的情况。

Go Module

在Go1.11中,Go提出了模块Module的概念,模块是由多个依赖包组成的有版本签名的集合。
Go Module通过配置文件描述依赖关系、通过中心仓库管理依赖库、通过本地工具管理依赖,也就是go.mod、proxy和go get\mod这三者。
通过这三个要素,Go Module实现了版本控制和隔离项目依赖的终极目标。

Go Module简易实践

首先,我们可以设置Go依赖管理的行为,在终端中输入go envimage.png 注意到其中三个变量GOPATH GO111MODULE GOPROXY
其中GO111MODULE变量有三个可选值:

  1. auto:是该变量的默认值,表示在GOPATH之外的目录使用Module管理依赖,在GOPATH中使用src管理依赖。
  2. off:关闭Module,使用GOPATH方案管理依赖。
  3. on:开启Module,使用go.mod文件配置依赖。

我们可以使用go env -w GO111MODULE='on'打开Module方案。

依赖配置go.mod

首先,我们需要初始化go.mod文件,也就是在项目目录中生成依赖配置文件
使用go mod init [module path]初始化依赖配置文件
例如:go mod init test image.png
初始的mod文件只包含module的path和所使用的Go的版本 image.png

接下来,我们使用go get工具来获取依赖包
go get -u gorm.io/gorm-u表示获取依赖包的最新版本,gorm.io/gorm则是依赖包的存储位置。

image.png

go.mod文件也会相应的更新。

image.png

require表示该项目所需要的依赖包的来源的路径。此时我们便可以在go文件中使用import引入已经安装好的依赖包。

GOPROXY

在使用go get安装依赖包时,可能会出现超时或安装缓慢的情况,此时我们可以更换Go Module的分发源来解决。
使用go env -w GOPROXY='xxx'来更换分发源。
国内可以使用

换源之后便可以正常使用Go Module管理依赖了