Go Modules 不完全教程

文章转载自公众号 Golang 成神之路 , 作者 L

Go Modules 是 Golang 官方最近几个版本推出的原生的包管理方式,在此之前,社区也不乏多种包管理方案。在讨论 Go Modules 之前,我们先回顾一下 Golang 的包管理历史的发展。然后讨论一下 Go Modules 的使用以及一些特性,篇幅有限,有些地方不方便展开,后面有时间再深入。行文仓促,不当之处,多多指教。

0. 包管理的历史

Golang 的包管理一直被大众所诟病的一个点,但是我们可以看到现在确实是在往好的方向进行发展。下面是官方的包管理工具的发展历史:

  • 在 1.5 版本之前,所有的依赖包都是存放在 GOPATH 下,没有版本控制。这个类似 Google 使用单一仓库来管理代码的方式。这种方式的最大的弊端就是无法实现包的多版本控制,比如项目 A 和项目 B 依赖于不同版本的 package,如果 package 没有做到完全的向前兼容,往往会导致一些问题。

  • 1.5 版本推出了 vendor 机制。所谓 vendor 机制,就是每个项目的根目录下可以有一个 vendor 目录,里面存放了该项目的依赖的 package。go build 的时候会先去 vendor 目录查找依赖,如果没有找到会再去 GOPATH 目录下查找。

  • 1.9 版本推出了实验性质的包管理工具 dep,这里把 dep 归结为 Golang 官方的包管理方式可能有一些不太准确。关于 dep 的争议颇多,比如为什么官方后来没有直接使用 dep 而是弄了一个新的 modules,具体细节这里不太方便展开。

  • 1.11 版本推出 modules 机制,简称 mod,也就是本文要讨论的重点。modules 的原型其实是 vgo,关于 vgo,可以参考文章末尾的参考链接。

除此之外,社区也一直在有几个活跃的包管理工具,使用广泛且具有代表性的主要有下面几个:

  • godep

  • glide

  • govendor

关于这几种包管理工具的使用这里就不再详述了。

1. modules 简单使用方式

下面看一下 modules 的简单使用方式。

1.1 准备工作

Golang 版本:1.12.3。在 1.12 版本之前,使用 Go modules 之前需要环境变量 GO111MODULE:

  • GO111MODULE=off: 不使用 modules 功能。

  • GO111MODULE=on: 使用 modules 功能,不会去 GOPATH 下面查找依赖包。

  • GO111MODULE=auto: Golang 自己检测是不是使用 modules 功能。

在 GOPATH 之外创建一个项目 mod-demo,包含一个 main.go 文件,内容如下:

packagemain
import("github.com/astaxie/beego")
funcmain() { beego.Run()}

1.2 初始化

初始化很简单,在项目根目录执行命令 go mod init mod-demo ,然后会生成一个 go.mod 文件如下。

➜ mod-demo $ gomod init mod-demogo: creating new go.mod: module .➜ mod-demo $ lsgo.mod     main.go➜ mod-demo $ catgo.modmodule .
go 1.12

这里比较关键的就是这个 go.mod 文件,这个文件中标识了我们的项目的依赖的 package 的版本。执行 init 暂时还没有将所有的依赖管理起来。我们需要将程序 run 起来(比如执行 go run/test),或者 build(执行命令 go build)的时候,才会触发依赖的解析。

比如使用 go run 即可触发 modules 工作。

➜ mod-demo $ gorun main.gogo: extracting github.com/astaxie/beego v1.12.02019/09/08 23:23:03.507 [I] http server Running on http://:8080

这个时候我们再查看 go.mod 文件:

➜ mod-demo $ catgo.modmodule mod-demo
go 1.12
require (github.com/astaxie/beego v1.12.0github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect)

同时我们发现项目目录下多了一个 go.sum 用来记录每个 package 的版本和哈希值。go.mod 文件正常情况会包含 module 和 require 模块,除此之外还可以包含 replace 和 exclude 模块。

这些 package 并不是直接存储到 $GOPATH/src,而是存储到 $GOPATH/pkg/mod 下面,不同版本并存的方式。

➜ mod-demo $ ls$GOPATH/pkg/mod/github.com/astaxiebeego@v1.11.0 beego@v1.11.1 beego@v1.12.0

1.3 依赖升级(降级)

可以使用如下命令来查看当前项目依赖的所有的包。

➜ mod-demo $ golist -m-uallgo: finding github.com/beego/x2j latestgo: finding github.com/cloudflare/golz4 latestgo: finding github.com/siddontang/go latestgo: finding github.com/shiena/ansicolor latestgo: finding github.com/couchbase/go-couchbase latestgo: finding github.com/siddontang/rdb latestgo: finding gopkg.in/check.v1 latestgo: finding github.com/siddontang/ledisdb latestgo: finding github.com/ssdb/gossdb latestgo: finding github.com/couchbase/gomemcached latestgo: finding github.com/wendal/errors latestgo: finding github.com/couchbase/goutils latestgo: finding golang.org/x/net latestgo: finding github.com/cupcake/rdb latestgo: finding github.com/beego/goyaml2 latestgo: finding golang.org/x/crypto latestgo: finding github.com/bradfitz/gomemcache latestgithub.com/Knetic/govaluate v3.0.0+incompatiblegithub.com/OwnLocal/goes v1.0.0github.com/astaxie/beego v1.12.0...

如果我想要升级(降级)某个 package 则只需要 go get 即可,比如:

go get package@version

需要注意的是,在 modules 模式开启和关闭的情况下,go get 的使用方式不是完全相同的。在 modules 模式开启的情况下,可以通过在 package 后面添加 @version 来表明要升级(降级)到某个版本。如果没有指明 version 的情况下,则默认先下载打了 tag 的 release 版本,比如 v0.4.5 或者 v1.2.3;如果没有 release 版本,则下载最新的 pre release 版本,比如 v0.0.1-pre1。如果还没有则下载最新的 commit。这个地方给我们的一个启示是如果我们不按规范的方式来命名我们的 package 的 tag,则 modules 是无法管理的。version 的格式为 v(major).(minor).(patch) ,更多信息可以参考:https://semver.org/ 。

比如我们现在想将我们依赖中的 beego 项目的版本改为 v1.11.1,则可以像如下操作。我们发现执行完 go get 之后, go.mod 中的项目的版本也相应改变了。

➜ mod-demo $ gogetgithub.com/astaxie/beego@v1.11.1go: finding github.com/astaxie/beego v1.11.1go: downloading github.com/astaxie/beego v1.11.1go: extracting github.com/astaxie/beego v1.11.1➜ mod-demo $ catgo.modmodule .
go 1.12
require (github.com/OwnLocal/goes v1.0.0 // indirectgithub.com/astaxie/beego v1.11.1 // indirectgithub.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect)

在 modules 开启的模式下,go get 还支持 version 模糊查询,比如 > v1.0.0 表示大于 v1.0.0 的可使用版本;< v1.12.0 表示小于 v1.12.0  版本下最近可用的版本。version 的比较规则按照 version 的各个字段来展开。

除了指定版本,我们还可以使用如下命名使用最近的可行的版本:

  • go get -u 使用最新的 minor 或者 patch 版本

  • go get -u=patch 使用最新的 patch 版本

1.4 vendor

我们知道 Go 1.5 推出了 vendor 机制,go mod 也可以支持 vendor 机制,将依赖包拷贝到 vendor 目录。但是像一些 test case 里面的依赖包并不会拷贝的 vendor 目录中。

➜ mod-demo $ gohelp mod vendorusage: go mod vendor [-v]
Vendor resets the main module's vendor directory to include all packagesneeded to build and test all the main module's packages.It does not include test code forvendored packages.

2. modules 特性

上面介绍了 go modules 的简单使用方法,但是 modules 的一些更高级的特性没有介绍,将在下面进行展开。

2.1 GoProxy

proxy 顾名思义,代理服务器。众所周知,有些 Golang 的 package 在国内是无法直接 go get 的。在之前,我们解决这个问题,一般都是通过设置 http_proxy/https_proxy 来解决。GoProxy 相当于官方提供了一种 proxy 的方式让用户来进行包下载。要使用 GoProxy 只需要设置环境变量 GOPROXY 即可。目前公开的 GOPROXY 有:

  • goproxy.io

  • goproxy.cn: 由七牛云提供,参考 github repo

当然你也可以实现自己的 GoProxy 服务,比如项目中的依赖包含外部依赖和内部依赖的时候,那么只需要实现 module proxy protocal 协议即可。

值得注意的是,在最新 release 的 Go 1.13 版本中默认将 GOPROXY 设置为 https://proxy.golang.org,这个对于国内的开发者是无法直接使用的。所以如果升级了 Go 1.13 版本一定要把 GOPROXY 手动改掉。

2.2 Replace

replace 主要为了解决某些包发生改名的问题。

对于另外一种场景有的时候也是有用的,比如对于有些 golang.org/x/ 下面的包由于某些原因在国内是下载不了的,但是对应的包在 github 上面是有一份拷贝的,这个时候我们就可以将 go.mod 中的包进行 replace 操作。

下面是一个 Beego 项目的 go.mod 的 replace 的示例。

replace golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85
replace gopkg.in/yaml.v2 v2.2.1 => github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d

2.3 SubCommand

modules 支持的 subcommand 如下。

➜ mod-demo $ gohelp modGo mod provides access to operations on modules.
Note that support formodules is built into all the go commands,not just 'go mod'. For example, day-to-day adding, removing, upgrading,and downgrading of dependencies should be doneusing 'go get'.See 'go help modules'foran overview of module functionality.
Usage:
go mod <command> [arguments]
The commands are:
download download modules to local cacheedit edit go.mod from tools or scriptsgraph print module requirement graphinit initialize new module incurrent directorytidy add missing and remove unused modulesvendor makevendored copy of dependenciesverify verify dependencies have expected contentwhy explain why packages or modules are needed
Use "go help mod <command>"formore information about a command.

每个 subcommand 的含义如下:

  • download: 下载 modules 到本地缓存

  • edit: 提供一种命令行交互修改 go.mod 的方式

  • graph: 将 module 的依赖图在命令行打印出来,其实并不是很直观

  • init: 初始化 modules,会生成一个 go.mod 文件

  • tidy: 清理 go.mod 中的依赖,会添加缺失的依赖,同时移除没有用到的依赖

  • vendor: 将依赖包打包拷贝到项目的 vendor 目录下,值得注意的是并不会将 test code 中的依赖包打包到 vendor 中。这种设计在社区也引起过几次争论,但是并没有达成一致。

  • verify: verify 用来检测依赖包自下载之后是否被改动过。

  • why: 解释为什么 package 或者 module 是需要,但是看上去解释的理由并不是非常的直观。

➜ mod-demo $ gomod why github.com/astaxie/beego# github.com/astaxie/beegomod-demogithub.com/astaxie/beego

3. Go 1.13 对 modules 的改动

上面在讨论 GoProxy 的时候提到了 Go 1.13 默认设置环境变量 GOPROXY 的值,除此之外 Go 1.13 对 modules 还有哪些值得注意的改动呢?

3.1 默认开启

modules 在 Go 1.13 的版本下是默认开启的。

3.2 GOPRIVATE

前面也说到对于一些内部的 package,GoProxy 并不能很好的处理,Go 1.13 推出了 GOPRIVATE 机制。只需要设置这个环境变量,然后标识出哪些 package 是 private 的,那么对于这个 package 的处理将不会从 proxy 下载。GOPRIVATE 的值是一个以逗号分隔的列表,支持正则(正则语法遵守 Golang 的 包 path.Match)。下面是一个 GOPRIVATE 的示例:

GOPRIVATE=*.corp.example.com,rsc.io/private

上面的 GOPRIVATE 表示以 *.corp.example.com 或者 rsc.io/private 开头的 package 都是私有的。

3.3 GOSUMDB

GOSUMDB 的全称为 Go CheckSum Database,用来下载的包的安全性校验问题。包的安全性在使用 GoProxy 之后更容易出现,比如我们引用了一个不安全的 GoProxy 之后然后下载了一个不安全的包,这个时候就出现了安全性问题。对于这种情况,可以通过 GOSUMDB 来对包的哈希值进行校验。当然如果想要关闭哈希校验,可以将 GOSUMDB 设置为 off;如果要对部分包关闭哈希校验,则可以将包的前缀设置到环境变量中 GONOSUMDB 中,设置规则类似 GOPRIVATE。

关于 GOSUMDB 的配置格式为:<db_name>+<publickey>+<url>。

GOSUMDB="sum.golang.org"GOSUMDB="sum.golang.org+<publickey>"GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"

上面三种配置都是合理的,因为对于 sum.golang.org,Go 自己知道其对应的 publickey 和 url,所以我们只要配置一个名字即可,对于另外一个 sum.golang.google.cn 也是一样。除此之外的,都需要指明 publickey,url 默认是 https://<db_name>。

关于 GOSUMDB 更多的详细信息可以参考:https://golang.org/cmd/go/#hdr-Module_authentication_failures

4. 总结

Golang 包管理历经多个版本,目前来看并没有一个完全让开发者满意的方案,比如类似 Java 的 maven 的包管理方式。很多开发者也表示 modules 的管理方式也不是很直观。其实 Golang 的 package 使用 git 的管理方式来管理其实是一个很好的管理方式,我们最需要的其实如何能让普通开发者获取到任何 package 的心智负担降到最低。值得欣慰的是我们确实有看到大家在这个方向上的努力,比如 modules 的 GoProxy。

放眼未来,希望会有一种令大部分普通开发者满意的包管理方式吧。

5. Reference

  1. https://research.swtch.com/vgo

  2. https://github.com/golang/go/wiki/Modules

  3. https://roberto.selbach.ca/intro-to-go-modules/

  4. https://roberto.selbach.ca/playing-with-go-modules/

  5. https://semver.org/

  6. https://golang.org/cmd/go/#hdr-Module_authentication_failures

  7. https://golang.org/doc/go1.13#modules

  8. https://goproxy.io

  9. https://github.com/goproxy/goproxy.cn

  10. https://codeengineered.com/blog/2018/golang-godep-to-vgo/

 

Go Modules使用教程的更多相关文章

  1. CSS Modules入门教程

    为什么引入CSS Modules 或者可以这么说,CSS Modules为我们解决了什么痛点.针对以往我写网页样式的经验,具体来说可以归纳为以下几点: 全局样式冲突 过程是这样的:你现在有两个模块,分 ...

  2. Environment Modules 简明教程

    Environment Modules 简明教程 1. Modules 简介 在 Linux 超算平台上,通常会安装有不同版本的多种编译器和其他软件等,如常用的编译器有 intel 和 gnu,常用的 ...

  3. Go Modules使用教程(3分钟学会)

    前言 随着Go 1.13发布,GOPROXY默认值proxy.golang.org在中国大陆不能被访问. 七牛云顺势推出goproxy.cn,以利于中国开发者更好使用Go Modules,它是非盈利性 ...

  4. CSS Modules入门及React中实践(内附webpack4配置)

    本篇文章以整理为主,自己进行了部分修改,如有侵权,请告知 CSS Modules介绍 CSS Modules是什么东西呢?首先,让我们从官方文档入手:GitHub – css-modules/css- ...

  5. 从零开始配置TypeScript + React + React-Router + Redux + Webpack开发环境

    转载请注明出处! 说在前面的话: 1.为什么不使用现成的脚手架?脚手架配置的东西太多太重了,一股脑全塞给你,我只想先用一些我能懂的库和插件,然后慢慢的添加其他的.而且自己从零开始配置也能学到更多的东西 ...

  6. vue+webpack 安装常见插件

    html-webpack-plugin 插件地址:https://www.npmjs.com/package...安装指令: npm install html-webpack-plugin --sav ...

  7. React.js - 入门

    React.js - 第1天 1. React简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 ...

  8. ant design pro (六)样式

    一.概述 参看地址:https://pro.ant.design/docs/style-cn 基础的 CSS 知识或查阅属性,可以参考 MDN文档. 二.详细介绍 2.1.less Ant Desig ...

  9. 你一定喜欢看的 Webpack 2.× 入门实战(转载)

    最近在学习 Webpack,网上大多数入门教程都是基于 Webpack 1.x 版本的,我学习 Webpack 的时候是看了 zhangwang 的 <<入门 Webpack,看这篇就够了 ...

随机推荐

  1. linux命令之head、tail命令详解

    head 语法 例子 tail 语法 例子 head和tail组合用法举例 head 语法 head [-n -k ]... [FILE]... 例子 默认是显示开头前10行. head /etc/p ...

  2. [Python Basics]引用系统(The Import System)

    欲上高楼去避愁,愁还随我上高楼.经行几处江山改,多少亲朋尽白头. 归休去,去归休.不成人总要封侯?浮云出处元无定,得似浮云也自由. 我从短暂的Python工作当中学到一件事,越是模块化,就越会发现py ...

  3. F#周报2019年第50期

    新闻 Azure Functions 3.0系统上线 GC性能架构--第1部分 ConfigureAwait问题解答 介绍System.Threading.Channels Windows Serve ...

  4. 【全栈修炼】396- OAuth2 修炼宝典

    一.OAuth 概念 开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用.-- 维基百科 ...

  5. Vue中兄弟组件间传值-(Bus/总线/发布订阅模式/观察者)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. HTML5变化

    HTML5变化 新的语义化元素 header footer nav main article section 删除了一些纯样式的标签 表单增强 新API 离线 (applicationCache ) ...

  7. 解决问题的能力 > 10倍程序员

    如果第二次看到我的文章,欢迎右侧扫码订阅我哟~ 

  8. DS-5新加交叉编译工具

    Adding New Compiler Toolchains to DS-5 In this tutorial, you will learn how to add new compiler tool ...

  9. 自定义滚动条(Custom ScrollBar)

    时间如流水,只能流去不流回! 点赞再看,养成习惯,这是您给我创作的动力! 本文 Dotnet9 https://dotnet9.com 已收录,站长乐于分享dotnet相关技术,比如Winform.W ...

  10. Windows下mysql-5.7.28下载、安装、配置教程

    最近需要更换mysql数据库的版本,写一篇文章,记录一下 一.下载mysql数据库 mysql的下载共有两种,一种是zip压缩文件,另一种是msi安装程序 官方5.7版本zip压缩文件下载页面 官方5 ...