Golang 语言 vendor 在 GOPATH 和 Modules 中的区别

简介: Golang 语言 vendor 在 GOPATH 和 Modules 中的区别

01

介绍

在 Golang 语言中,Golang 程序是由 Golang Package 组成的,go build 的过程实际上就是编译 Golang Package。本文我们介绍 Golang 构建模式主要演进的三个阶段,分别是 GOPATH、 引入 vendor 机制的 GOPATH 和 Go Module。

GOPATH

Golang 初期版本中就原生内置了 GOPATH 的构建模式,Golang 程序在编译时,Golang 编译器会在 GOPATH 环境变量配置的本地路径下,查找 Golang 程序依赖的三方包,如果依赖包不存在,go build 命令将返回错误「无法找到包 XXX」,此时,我们需要使用 go get 命令手动将 Golang 程序依赖的三方包下载到 GOPATH 环境变量配置的本地路径下,然后再尝试执行 go build 命令。

go get 命令虽然方便,它可以将依赖包以及依赖包的依赖包自动下载到 GOPATH 环境变量配置的本地路径下,但是读者朋友们需要注意的是,go get 命令下载的依赖包是当前依赖包的最新版本,如果我们对依赖包的版本有要求,就不能使用 go get 命令。

比如在多人开发的 Golang 项目中,新加入成员将 Golang 项目下载到本地,使用 go get 命令下载依赖包时,正好赶上依赖包的版本更新了,此时,新成员使用 go build 命令构建 Golang 程序,Golang 程序也将使用最新版本的三方依赖包。如果三方依赖包存在 bug 或不向下兼容,将直接影响 Golang 程序的稳定性。

03

Vendor

Golang 官方为了解决 go get 命令会下载最新版本依赖包的问题,在 Golang v1.5 版本中引入 vendor 机制。

所谓 vendor 机制,就是在 Golang 项目的目录中,创建一个目录名为 vendor 的目录,将 Golang 项目的所有依赖包缓存到该目录中。

Golang 程序在编译时,Golang 编译器会优先在 vendor 目录中查找 Golang 程序依赖的三方包,而不是在 GOPATH 环境变量配置的本地路径下查找。

我们只需将 vendor 目录一起提交到代码仓库中,新成员在下载 Golang 项目后,构建项目就不会改变三方依赖包的版本。

读者朋友们需要注意的是,vendor 机制也引入了新的问题,比如想要使用 vendor 机制,Golang 项目必须在 GOPATH 环境变量配置的本地路径下的 src 目录中。

随着项目不断迭代,依赖的三方包也会越来越多,vendor 目录会变得越来越大,将 vendor 目录提交到代码仓库,不仅会影响下载代码的速度,还会影响 Code Review。

此外,vendor 目录中的三方依赖包,也需要我们手动管理,比如手动记录依赖三方包的版本号,手动下载三方依赖包等。

Golang 社区为了解决 vendor 机制引入的问题,推出一些比较流行的解决三方包依赖管理的工具,比如 dep、gb、glide 等三方包依赖管理工具,但是这些社区推出的三方包依赖管理工具都有各自的问题。

04

Modules

Golang 官方在总结 Golang 社区推出的各种三方包依赖管理工具遇到的问题的基础上,在 Golang v1.11 版本中,推出 Go Module 构建模式。

关于 Go Module 构建模式,官方 blog 有相关系列文章介绍,在之前的公众号文章中,也有官方 blog 发表的 Go Module 相关文章的译文。感兴趣的读者朋友可以按需翻阅一下。

Go Module 构建模式可以将 Golang 项目代码放在任意目录,无需同 vendor 机制一样,必须将 Golang 项目代码放在 GOPATH 环境变量配置的本地目录下的 src 目录中。

因为 Golang 官方为了同时支持 GOPATH 构建模式和 Go Module 构建模式,在 Golang v1.11 版本中,Go Module 构建模式默认是「关闭」,除非手动开启 Go Module 构建模式,如果将 Go Module 构建模式设置为「自动」,而 Golang 项目在 GOPATH 环境变量配置的目录中的 src 目录下,go build 命令优先使用 GOPATH 构建模式。

在 Golang v.13 版本中,Go Module 构建模式默认是「自动」,不管 Golang 项目在不在 GOPATH 环境变量配置的本地目录中的 src 目录下,只要项目根目录中包含 go.mod 文件,就启用 Go Module 构建模式,否则启用 GOPATH 构建模式。

所以在 Golang v1.13 版本之前,如果想要使用 Go Module 构建模式,那么 Golang 项目代码不可以放在 $GOPATH/src 目录中。

在 Golang v1.16 版本开始,Golang 已经默认开启 Go Module 构建模式,未来 Golang 官方还会考虑彻底移除 GOPATH 构建模式,我建议读者朋友们现在开始将 GOPATH 构建模式的老项目迁移到 Go Module 构建模式,并且在新项目中直接使用 Go Module 构建模式。

Golang 项目使用 Go Module 构建模式,那么还需要使用 vendor 机制吗?答案是需要,因为 vendor 机制可以将 Golang 项目依赖的三方包缓存到 vendor 目录,这样在无法访问网络的环境下,我们可以在 vendor 机制下使用 Go Module 构建模式,或者在构建性能敏感的环境中使用 Go Module 构建模式,比如在使用内部 CI 工具构建 Golang 程序时。

在 Go Module 构建模式下,vendor 机制无需像在 GOPATH 构建模式下,需要我们手动管理三方依赖包的版本和下载,Golang 提供了 go mod vendor 命令,帮助我们创建和管理 vendor 目录。

在我们想要基于 vendor 目录缓存的三方依赖包构建 Golang 程序,而不是基于本地缓存的 Go Module 构建 Golang 程序时,可以在 go build 命令后面加上 -mod=vendor 参数。并且在 Golang v1.14 及以后的版本中,如果 Golang 项目根目录下存在 vendor 目录,go build 命令会默认优先基于 vendor 目录缓存的三方依赖包构建 Golang 程序,除非我们在 go build 命令后面加上 -mod=mod 参数。

05

总结

本文我们介绍了 Golang 的构建模式的演进过程,Golang 官方先后推出三种构建模式,分别是 GOPATH 构建模式,引入 vendor 机制的 GOPATH 构建模式和 Go Module 构建模式。

介绍了为什么要引进 vendor 机制,它解决了什么问题,同时它又带来了什么问题。官方基于社区推出的三方依赖包版本管理工具的基础上推出 Go Module 构建模式,是怎么解决 GOPATH 构建模式中 vendor 机制引入的问题的。

介绍了 vendor 机制在 GOPATH 构建模式和在 Go Module 构建模式中使用的区别是什么,为什么在 Go Module 构建模式中仍然需要使用 vendor 机制,如何在 Go Module 构建模式中使用 vendor 机制。

推荐阅读:

参考资料:

https://go.googlesource.com/proposal/+/master/design/25719-go15vendor.md 

https://pkg.go.dev/cmd/go#hdr-GOPATH_and_Modules 


目录
相关文章
|
2月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
114 4
Golang语言之管道channel快速入门篇
|
2月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
67 4
Golang语言文件操作快速入门篇
|
2月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
104 3
Golang语言之gRPC程序设计示例
|
2月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
86 4
|
2月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
52 4
Golang语言goroutine协程篇
|
2月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
55 3
Golang语言之Prometheus的日志模块使用案例
|
1月前
|
前端开发 中间件 Go
实践Golang语言N层应用架构
【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。
31 0
|
2月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
48 3
|
2月前
|
Go
Golang语言之函数(func)进阶篇
这篇文章是关于Golang语言中函数高级用法的教程,涵盖了初始化函数、匿名函数、闭包函数、高阶函数、defer关键字以及系统函数的使用和案例。
64 3
Golang语言之函数(func)进阶篇
|
2月前
|
Go
Golang语言之函数(func)基础篇
这篇文章深入讲解了Golang语言中函数的定义和使用,包括函数的引入原因、使用细节、定义语法,并通过多个案例展示了如何定义不返回任何参数、返回一个或多个参数、返回值命名、可变参数的函数,同时探讨了函数默认值传递、指针传递、函数作为变量和参数、自定义数据类型以及返回值为切片类型的函数。
62 2
Golang语言之函数(func)基础篇