美文网首页
go modules 基础

go modules 基础

作者: _张晓龙_ | 来源:发表于2020-07-14 07:05 被阅读0次

    引言

    go 语言支持 go modules 特性已经快两年了,但很多项目由于种种原因还没有实施迁移。这几天想写一篇文章,全面的阐述一下 go modules 特性,使得读者快速掌握其要点,从而低成本的将项目的包管理迁移到 go modules。

    考虑到实战部分的内容较多,将计划的一篇文章拆分为多篇来呈现,一层层的慢慢揭开 go modules 的神秘面纱。本文为开篇,主要介绍 go modules 的基础知识。

    go modules 背景

    在 go1.5 发布后的若干年,gophers 把注意力都集中在如何利用 vendor 机制来解决包依赖问题,从手工添加依赖到 vendor、手工更新依赖,到一众包依赖管理工具的诞生,比如: govendorglide 以及号称准官方工具的 dep,都在努力地尝试着按照当时的主流思路来解决诸如“钻石型依赖”等难题。

    正当 gophers 认为 dep 将“顺理成章”地转正为 go toolchain 一部分的时候,vgo 横空出世,并通过对 “Semantic Import Versioning” 和 ”Minimal Version Selected” 的设定,在原 go tools上简单快速地实现了 go 原生的包依赖管理方案 这个 vgo 就是 go modules 的前身。

    go modules 定义

    通常,我们会在一个 go 代码库中创建一组包,而代码库的路径会作为包的导入路径,比如 github.com/agiledragon/gomonkey。在 go1.11 版本中,给在同一代码库下面的这样一组包定义了一个新的概念叫 modulemodule 根目录一般叫 module root 目录。

    一个代码库只有一个 module 吗?一般情况是这样,但也可以有多个 module

    go mod可以看作是 go modules 的简称。

    go mod 配置

    go1.11 在引入 go modules 特性时,增加了一个特性开关(环境变量)GO111MODULE,对应的值有三个:auto,on,off。

    下面详细解释一下这三个值的含义:

    • 当 GO111MODULE 的值为 off 时,go modules 机制关闭,go 编译器会始终使用 GOPATH mode,即无论要构建的源码目录是否在 GOPATH 路径下,go 编译器都会在传统的 GOPATH 和 vendor 目录下搜索目标程序依赖的包;
    • 当 GO111MODULE 的值为 on 时,go modules 机制始终开启,与off 相反,go 编译器会始终使用 module-aware mode,即无论要构建的源码目录是否在 GOPATH 路径下,go 编译器都不会在传统的 GOPATH 和 vendor 目录下搜索目标程序依赖的包,而是在 go mod 命令的缓存目录($GOPATH/pkg/mod)下搜索对应版本的依赖包;
    • 当 GO111MODULE 的值为 auto 时(默认值),使用 GOPATH mode还是 module-aware mode,取决于要构建的源码目录所在位置以及是否包含 go.mod 文件:a. 对于 go1.11 和 go1.12 版本,如果要构建的源码目录不在以 GOPATH/src 为根的目录体系下,且包含go.mod文件(两个条件缺一不可),那么使用 module-aware mode,否则使用传统的 GOPATH mode;b. 对于 go1.13及以上版本,如果要构建的源码目录包含 go.mod 文件,那么使用 module-aware mode,否则使用传统的 GOPATH mode。

    GOPATH mode & GOPATH 环境变量

    使用 module-aware mode 后,用户可以不用再配置 GOPATH 环境变量了,但细心的读者可能已经发现 go mod 命令的缓存目录为 $GOPATH/pkg/mod

    那到底还要不要配置GOPATH 环境变量了?

    从 go1.8 版本开始,GOPATH 有了默认值,即 $HOME/go。如果用户配置了 GOPATH 环境变量,则覆盖默认值。所以,在 module-aware mode 下,不管用户是否配置了 GOPATH 环境配置,$GOPATH 都有确定的值,无非是否为默认值的问题,所以 go mod 命令的缓存目录 $GOPATH/pkg/mod 是有效的。

    综上,使用 go modules 机制管理依赖包后,我们弃用的是 GOPATH mode,而不是 GOPATH 环境变量,当然你也可以不设置 GOPATH 环境变量,go 编译器直接使用 GOPATH 默认值也 OK。

    go mod 命令

    go 编译器提供了 go mod 命令来管理依赖包。

    go mod 主要包括下命令:

    命令 说明
    download download modules to local cache(下载依赖包)
    edit edit go.mod from tools or scripts(编辑go.mod)
    graph print module requirement graph (打印模块依赖图)
    init initialize new module in current directory(在当前目录初始化mod)
    tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块)
    vendor make vendored copy of dependencies(将依赖复制到vendor下)
    verify verify dependencies have expected content (验证依赖是否正确)
    why explain why packages or modules are needed(解释为什么需要依赖)

    go mod 文件

    go mod 文件主要包括 go.mod 和 go.sum,这两个文件在 module root 目录下,都要提交到代码库。

    使用 go mod 管理依赖后,在 module root 目录下:

    • 运行 go mod init 命令会自动生成 go.mod 文件,该文件记录当前代码工程的所有依赖库及版本,可以通过 go 的命令来维护
    • 运行 go build 或 go run 命令会自动生成 go.sum 文件,该文件记录每个依赖库特定版的哈希值,可以确保下次获取的第三方依赖与本次相同

    go.mod 文件提供了 module,require,replace 和 exclude 等命令:

    • module 语句指定包的名字(路径)
    • require 语句指定依赖的模块列表
    • replace 语句可以替换某个 require 指定的模块
    • exclude 语句可以忽略某个 require 指定的模块

    goconvey 测试框架的 go.mod 文件:

    module github.com/smartystreets/goconvey
    
    require (
        github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
        github.com/jtolds/gls v4.20.0+incompatible
        github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d
        golang.org/x/tools v0.0.0-20190328211700-ab21143f2384
    )
    
    

    goconvey 测试框架的 go.sum 文件:

    github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
    github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
    github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
    github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
    github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
    github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
    golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
    golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
    golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
    golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
    golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
    golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
    

    go proxy

    顾名思义,go proxy 是 go 语言的代理,一般是配合 go modules 使用,可以使用户方便且快速的获取第三方包,尤其是 go 官方的一些包。

    作者本机配置的 go proxy 为:

    bogon:~ zhangxiaolong$ echo $GOPROXY
    https://goproxy.cn,direct
    

    go 团队包管理从传统 GOPATH mode 迁移到 module-aware mode 后,代码库中没有了 vendor 目录,而增加了 go.mod 文件和 go.sum 文件。在 CI 流水线上编译版本时,需要实时获取依赖,但从外网下载所有依赖的第三方包有一些问题:

    • 很慢(外网速度比内网速度慢太多)
    • 不方便(有的环境无法上外网)
    • 下载失败(墙的原因)

    此时,在公司内网搭建一个私有的 go proxy 就使得这些问题迎刃而解。

    小结

    本文主要介绍了 go modules 的基础知识,包括背景、定义、配置、模式、命令、文件和代理等内容,希望对读者感性认识 go modules 特性有一定的帮助。

    下一篇文章将开启实战部分,作者将使用 go1.14 版本,通过一些小案例来初步解析 go modules 机制,名字叫啥好呢?
    “什么天长地久,只是随便说说,你爱我那一点,你也说不出口......”。突然,听到了张震岳的成名曲《爱的初体验》,要不下一篇文章就叫 《go modules 初体验》

    相关文章

      网友评论

          本文标题:go modules 基础

          本文链接:https://www.haomeiwen.com/subject/okyvcktx.html