内容简介:我从2016年毕业的时候,第一份工作,就接触了之后2017年新年刚过,我跑到了深圳,在华为外包做
1. 前言
Golang
是一门到如今有十年的静态高级语言了,2009年的时候算是正式推出了,然后到最近的一两年,2017-2018年的时候,突然直线上升,爆火了,得益于容器化运维/直播/短视频/区块链...
我从2016年毕业的时候,第一份工作,就接触了 Golang
语言,那时公司用 Java
处理数据流,然后用 Golang
写高性能的网络中间件。那时, Golang
还很简单,那时 Docker
刚火, Kubernates
还在发力中,基本在中国,还算小众语言。
之后2017年新年刚过,我跑到了深圳,在华为外包做 Kubernates
二次开发,全职了 Golang
,接触了专业的开发模式。那年,区块链火了,那年,编排系统,容器化运维火了。到了2018,越来越多的人用了 Golang
,越來越多的大公司开始招 Golang
。
Golang
语法简单,简单即是复杂,软件构建的核心在于将复杂的东西简单化,处理好复杂度。
作为一个 gopher
,我们要知道他的包管理,这样才能合理化代码结构,做好工程管理。( gopher
:地鼠)
2. GOPATH/ Golang 1.5之前
Golang
的包管理一直让人口病,一开始它用 GOPATH
来进行依赖库管理,特别简单粗暴。
如果环境变量:
export GOROOT=/home/love/go export GOPATH=/home/love/code export GOBIN=$GOROOT/bin
上面 GOROOT
是指 Golang编译器以及其 工具 链,基础源码库
所在的目录, GOPATH
是用户自定义的代码所在位置。
以下 GOPATH
的结构如下:
├── src
└── github.com
└── hunterhug
└── rabbit
└── a
└── a.go
└── main.go
├── bin
├── pkg
我们写的开发包有简单易懂的路径之分,比如我的包叫 github/hunterhug/rabbit
,那么结构如上面一样。
我们进入到 rabbit
目录, main.go
代码:
package main
import "github/hunterhug/rabbit/a"
func main(){
...
}
然后 go build
的话,找包时,就会从 GOPATH src
下面开始找,比如 rabbit
包下的 main.go
依赖了 github/hunterhug/rabbit/a
,那么它首先从 src
下面按路径往下拼接查找,然后就找到了,最后生成和包名 github/hunterhug/rabbit
一样的一个叫 rabit
的二进制。
如果我们 go install
的话,这个二进制就会保存在 GOBIN
下(如果不存在 GOBIN
,会保存在 GOPATH bin
下)。如果我们要编译时,缺少包,那么 go get -v
将会下载依赖包源码到 GOPATH src
下,然后在 GOPATH pkg
目录下生成该包的静态库(下次用就不用再从源码编译了,算缓存)。
但是我们包找不到时:
love@love:~/code/src/github.com/hunterhug/fafacms$ go build
core/server/server.go:4:2: cannot find package "github.com/gin-contrib/cors" in any of:
/home/love/go/src/github.com/gin-contrib/cors (from $GOROOT)
/home/love/code/src/github.com/gin-contrib/cors (from $GOPATH)
我们发现,原来,其实是先去 GOROOT
下找包,找不到包,再去 GOPATH
找,流下了感动的泪水!比如我们的 GOPATH
下建了一个 fmt
包:
package fmt
func PPrintln() {
print("i am diy fmt")
}
但是我们想引用这个库, main.go
使用:
package main
import fmt
func main(){
fmt.PPrintln()
}
发现引用不了,2333! 所以, GOPATH
下的包最好不要和 GOROOT
下的标准库重名!
你再看下 GOROOT
的结构:
├── src
└── time
└── fmt
├── bin
├── pkg
这不和我们的 GOPATH
很像吗,对,现在的 Golang编译器
是自编译的,就是用 Golang
来写 Golang编译器
,它的编译器及中间产物,基础库等,保持和 GOPATH
一毛一样,无缝衔接。
但是不同依赖包是有版本的,版本变了怎么办?这就需要人工管理了。
3. Golang vendor/Golang1.5以后
自己管理库版本,想想都不太可能,毕竟 Java
有 maven
, Python
有 pip
, PHP
有 compose
, NodeJs
有 npm
。
于是从 Golang1.5
开始推出 vendor
文件夹机制( vendor
:供应商/小贩)。
从 Golang1.6
正式开启这个功能。
比如我们的包叫 awesomeProject
,在 GOPATH
下结构:
├── src
└── awesomeProject
└── vendor
└── fmt
└── fmt.go
└── main.go
├── pkg
其中 main.go
:
package main
import "fmt"
func main() {
fmt.PPrintln()
}
我们进入 awesomeProject
目录,并且 go build
, 偶也成功。
这下子不会像上面没 vendor
时直接引用 GOROOT
的标准包了,我们终于可以用和标准包重名的包了,那就是放在和 main.go
同目录的 vendor
下面!
这下子,我们 import
的包会先在同级 vendor
下找,找不到再按照以前的方式。
如果我们将 main
改成引用一个不存在的包 b
:
package main
import (
"b"
)
func main() {
b.P()
}
然后 go build
提示:
main.go:4:2: cannot find package "b" in any of:
/home/love/code/src/awesomeProject/vendor/b (vendor tree)
/home/love/go/src/b (from $GOROOT)
/home/love/code/src/b (from $GOPATH)
如果此时我们再任性一点,在 GOPATH src
下建立一个空的 vendor
文件夹,则会提示:
main.go:4:2: cannot find package "b" in any of:
/home/love/code/src/awesomeProject/vendor/b (vendor tree)
/home/love/code/src/vendor/b
/home/love/go/src/b (from $GOROOT)
/home/love/code/src/b (from $GOPATH)
好了,我们发现现在的加载方式是:
包同目录下的vendor GOPATH src 下的vendor GOROOT src GOPATH src
如果在 GOROOT
和 GOPATH
下建 vendor
会怎么样?我们就不止疼了,233。。
好了,现在问题就是 vendor
是怎么冒泡的,如果我 main.go
引用了 vendor/b
,而 b
包里面引用了一个 c
包。此时 vendor/b
会怎么找库?
├── src
└── awesomeProject
└── vendor
└── b
└── b.go
└── main.go
├── pkg
现在 vendor/b/b.go
的内容:
package b
import "c"
func P() {
print(" i am vendor b\n")
c.P()
}
我们进入 awesomeProject
项目 go build
,出现:
vendor/b/b.go:3:8: cannot find package "c" in any of:
/home/love/code/src/awesomeProject/vendor/c (vendor tree)
/home/love/code/src/vendor/c
/home/love/go/src/c (from $GOROOT)
/home/love/code/src/c (from $GOPATH)
现在加载流程是:
包同目录的包(即b包同目录看看有没有c包) GOPATH src 下的vendor GOROOT src GOPATH src
此时我们在 vendor/b
下建一个空 vendor
:
├── src
└── awesomeProject
└── vendor
└── b
└── vendor
└── b.go
└── main.go
├── pkg
进入 awesomeProject
项目再 go build
会出现:
vendor/b/b.go:3:8: cannot find package "c" in any of:
/home/love/code/src/awesomeProject/vendor/b/vendor/c (vendor tree)
/home/love/code/src/awesomeProject/vendor/c
/home/love/code/src/vendor/c
/home/love/go/src/c (from $GOROOT)
/home/love/code/src/c (from $GOPATH)
如果我们再满足上面的 c
包,同理在 c
包建一个空 vendor
:
├── src
└── awesomeProject
└── vendor
└── b
└── vendor
└── c
└── vendor
└── c.go
└── b.go
└── main.go
├── pkg
但 c
包 c.go
引用了不存在的 d
包:
package c
import "d"
func P() {
d.P()
}
进入 awesomeProject
项目再 go build
会出现:
vendor/b/vendor/c/c.go:3:8: cannot find package "d" in any of:
/home/love/code/src/awesomeProject/vendor/b/vendor/c/vendor/d (vendor tree)
/home/love/code/src/awesomeProject/vendor/b/vendor/d
/home/love/code/src/awesomeProject/vendor/d
/home/love/code/src/vendor/d
/home/love/go/src/d (from $GOROOT)
/home/love/code/src/d (from $GOPATH)
发现, 查找包 vendor
是往上冒泡的, 一个包引用另一个包,先看看 同目录 vendor
下有没有这个包, 没有的话一直追溯到上一层 vendor
看有没有,没有的话再上一层
vendor
,直到 GOPATH src/vendor
。
所以现在的加载流程是:
包同目录下的vendor 包目录向上的最近的一个vendor ... GOPATH src 下的vendor GOROOT src GOPATH src
总结: vendor
向上冒泡!!!!
这样的话, 我们可以把包的依赖都放在 vendor
下,然后提交到仓库,这样可以省却拉取包的时间,并且相对自由,你想怎么改都可以,你可以放一个已经被人删掉的 github
包在 vendor
下。这样,依然手动,没法管理依赖版本。
所以很多第三方,比如 glide
, godep
, govendor
工具出现了, 使用这些工具, 依赖包必须有完整的 git
版本, 然后会将所有依赖的版本写在一个配置文件中。
比如 godep
:
go get -v github.com/tools/godep
在包下执行
godep save
会生成 Godeps/Godep.json
记录依赖版本,并且将包收集于 当前 vendor
下。
3. go mod/Go1.11以后
Golang 1.11
开始, 实验性出现了可以不用定义 GOPATH
的功能,且官方有 go mod
支持。 Golang 1.12
更是将此特征正式化。
现在用 Golang1.12
进行:
go mod init go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'
其中 GO111MODULE=auto
是一个开关,开启或关闭模块支持,它有三个可选值: off
/ on
/ auto
,默认值是 auto
。
-
GO111MODULE=off,无模块支持,和之前一样。 -
GO111MODULE=on,模块支持,忽略GOPATH和vendor文件夹,只根据go.mod下载依赖。 -
GO111MODULE=auto,该项目在GOPATH src外面且根目录有go.mod文件时,开启模块支持。
在使用模块的时候, GOPATH
是无意义的,不过它还是会把下载的依赖储存在 GOPATH/src/mod
中,也会把 go install
的结果放在 GOPATH/bin
(如果 GOBIN
不存在的话)
我们将项目移出 GOPATH
,然后:
go mod init
出现:
go: cannot determine module path for source directory /home/love/awesomeProject (outside GOPATH, no import comments)
现在 main.go
改为:
package main // import "github.com/hunterhug/hello"
import (
"b"
)
func main() {
b.P()
}
将会生成 go.mod
:
module github.com/hunterhug/hello go 1.12
此时我们:
go build build github.com/hunterhug/hello: cannot load b: cannot find module providing package b
这下没法查找 vendor
了,我们加上参数再来:
go build -mod=vendor build github.com/hunterhug/hello: cannot load c: open /home/love/awesomeProject/vendor/c: no such file or directory
流下了感动的泪水, vendor
冒泡呢?原来启用了 go.mod
, vendor
下的包 b
无法找到 b/vendor
下的包 c
,只能找到一级,2333333,这是好还是坏?
一般情况下, vendor
下面有 vendor
是不科学的, godep
等工具会将依赖理顺,确保只有一个 vendor
。
那么 go.mod
导致 vendor
无法冒泡产生的影响,一点都不大,流下感动的泪水。
现在我们来正确使用 go mod
, 一般情况下:
待写
待写
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 技术管理者标准管理模板
- VMware管理集群可协助管理
- MySQL用户账户管理与权限管理详解
- 项目管理基础:什么是项目管理?
- 软件管理理论:目标管理 & SMART 原则
- Go 语言内存管理(二):Go 内存管理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Data Structures and Algorithm Analysis in Java
Mark A. Weiss / Pearson / 2006-3-3 / USD 143.00
As the speed and power of computers increases, so does the need for effective programming and algorithm analysis. By approaching these skills in tandem, Mark Allen Weiss teaches readers to develop wel......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!