哈喽,大家好, 一般编程语言都会提供依赖库管理工具,例如python的pip、node.js的npm,java的maven,rust的cargo,Go语言也有提供自己的依赖库管理工具,Go语言在1.11提出了Go mod,每次版本或多或少都会对go.mod进行改进优化,go mod也越来越好,当前大多数公司都使用go mod来管理依赖库,所以本文我们一起来入门go mod(参考资料文末给出);
起初Go语言在1.5之前没有依赖管理工具,若想引入依赖库,需要执行go get命令将代码拉取放入GOPATH/src目录下,作为GOPATH下的全局依赖,这也就意味着没有版本控制及隔离项目的包依赖;
为了解决隔离项目的包依赖问题,Go1.5版本推出了vendor机制,环境变量中有一个GO15VENDOREXPERIMENT需要设置为1,该环境变量在Go1.6版本时变成默认开启,目前已经退出了历史舞台;
vendor其实就是将原来放在GOPATH/src的依赖包放到工程的vendor目录中进行管理,不同工程独立地管理自己的依赖包,相互之间互不影响,原来是包共享的模式,通过vendor这种机制进行隔离,在项目编译的时候会先去vendor目录查找依赖,如果没有找到才会再去GOPATH目录下查找;
优点:保证了功能项目的完整性,减少了下载依赖包,直接使用vendor就可以编译
缺点:仍然没有解决版本控制问题,go get仍然是拉取最新版本代码;
很多优秀的开发者在这期间也都实现了不错的包依赖管理工具,例如:
godep:https://github.com/tools/godep
govendor:https://github.com/kardianos/govendor
glide:https://github.com/Masterminds/glide
dep:https://github.com/golang/dep
dep应该是其中最成功的,得到了Go语言官方的支持,该项目也被放到了https://github.com/golang/dep,但是为什么dep没有称为官宣的依赖工具呢?
其实因为随着Russ Cox 与 Go 团队中的其他成员不断深入地讨论,发现 dep 的一些细节似乎越来越不适合 Go,因此官方采取了另起 proposal 的方式来推进,其方案的结果一开始先是释出 vgo,最终演变为我们现在所见到的 Go modules;
go modules是Russ Cox推出来的,发布于Go1.11,成长于Go1.12,丰富于Go1.13,正式于Go1.14推荐在生产上使用,几乎后续的每个版本都或多或少的有一些优化,在Go1.16引入go mod retract、在Go1.18引入go work工作区的概念,这些我们在本文都会介绍到;
这个环境变量是Go Modules的开关,主要有以下参数:
我现在使用的Go版本是1.19.3,默认GO111MODULE=on,感觉该变量也会像GO15VENDOREXPERIMENT最终推出系统环境变量的舞台;
该环境变量用于设置Go模块代理,Go后续在拉取模块版本时能够脱离传统的VCS方式从镜像站点快速拉取,GOPROXY的值要以英文逗号分割,默认值是https://proxy.golang.org,direct
,但是该地址在国内无法访问,所以可以使用goproxy.cn
来代替(七牛云配置),设置命令:
go env -w GOPROXY=GOPROXY=https://goproxy.cn,direct
也可以使用其他配置,例如阿里配置:
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/
该环境变量也可以关闭,可以设置为"off",禁止Go在后续操作中使用任何Go module proxy;
上面的配置中我们用逗号分割后面的值是direct
,它是什么意思呢?
direct
为特殊指示符,因为我们指定了镜像地址,默认是从镜像站点拉取,但是有些库可能不存在镜像站点中,direct
可以指示Go回源到模块版本的源地址去抓取,比如github,当go module proxy返回404、410这类错误时,其会自动尝试列表中的下一个,遇见direct
时回源地址抓取;
该环境变量的值是一个Go checksum database,用于保证Go在拉取模块版本时拉取到的模块版本数据未经篡改,若发现不一致会中止,也可以将值设置为off
即可以禁止Go在后续操作中校验模块版本;
什么是Go checksum database?
Go checksum database主要用于保护Go不会从任何拉到被篡改过的非法Go模块版本,详细算法机制可以看一下:https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md#proxying-a-checksum-database
GOSUMDB的默认值是sum.golang.org
,默认值与自定义值的格式不一样,默认值在国内是无法访问,这个值我们一般不用动,因为我们一般已经设置好了GOPROXY,goproxy.cn支持代理sum.golang.org
;
GOSUMDB的值自定义格式如下:
<SUMDB_NAME>+<PUBLIC_KEY>
。<SUMDB_NAME>+<PUBLIC_KEY> <SUMDB_URL>
。这三个环境变量放在一起说,一般在项目中不经常使用,这三个环境变量主要用于私有模块的拉取,在GOPROXY、GOSUMDB中无法访问到模块的场景中,例如拉取git上的私有仓库;
GONOPROXY、GONOSUMDB的默认值是GOPRIVATE的值,所以我们一般直接使用GOPRIVATE即可,其值也是可以设置多个,以英文逗号进行分割;例如:
$ go env -w GOPRIVATE="github.com/asong2020/go-localcache,git.xxxx.com"
也可以使用通配符的方式进行设置,对域名设置通配符号,这样子域名就都不经过Go module proxy和Go checksum database;
go mod download会将依赖缓存到本地,缓存的目录是GOPATH/pkg/mod/cache
、GOPATH/pkg/sum
,这些缓存依赖可以被多个项目使用,未来可能会迁移到$GOCACHE
下面;
可以使用go clean -modcache
清理所有已缓存的模块版本数据;
我们可以使用go help mod
查看可以使用的命令:
$ go env -w GOPRIVATE="github.com/asong2020/go-localcache,git.xxxx.com"
命令 | 作用 |
---|---|
go mod init | 生成go.mod文件 |
go mod download | 下载go.mod文件中指明的所有依赖放到全局缓存 |
go mod tidy | 整理现有的依赖,添加缺失或移除不使用的modules |
go mod graph | 查看现有的依赖结构 |
go mod edit | 编辑go.mod文件 |
go mod vendor | 导出项目所有的依赖到vendor目录 |
go mod verify | 校验一个模块是否被篡改过 |
go mod why | 解释为什么需要依赖某个模块 |
go.mod是启用Go modules的项目所必须且最重要的文件,其描述了当前项目的元信息,每个go.mod文件开头符合包含如下信息:
module:用于定义当前项目的模块路径(突破$GOPATH路径)
go:当前项目Go版本,目前只是标识作用
require:用设置一个特定的模块版本
exclude:用于从使用中排除一个特定的模块版本
replace:用于将一个模块版本替换为另外一个模块版本,例如chromedp使用golang.org/x/image这个package一般直连是获取不了的,但是它有一个github.com/golang/image的镜像,所以我们要用replace来用镜像替换它
restract:用来声明该第三方模块的某些发行版本不能被其他模块使用,在Go1.16引入
例子:
image-20230102181623314
接下来我们分模块详细介绍一下各部分;
go.mod文件的第一行是module path,采用仓库+module name的方式定义,例如上面的项目:
module github.com/asong2020/go-localcache
因为Go module遵循语义化版本规范2.0.0,所以如果工程的版本已经大于2.0.0,按照规范需要加上major的后缀,module path改成如下:
module github.com/asong2020/go-localcache/v2
module github.com/asong2020/go-localcache/v3
......
go.mod文件的第二行是go version,其是用来指定你的代码所需要的最低版本:
go 1.19.3
其实这一行不是必须的,目前也只是标识作用,可以不写;
require用来指定该项目所需要的各个依赖库以及他们的版本,从上面的例子中我们看到版本部分有不同的写法,还有注释,接下来我们来解释一下这部分;
github.com/davecgh/go-spew v1.1.0 // indirect
以下场景才会添加indirect注释:
Go1.17版本对此做了优化,indirect 的 module 将被放在单独 require 块的,这样看起来更加清晰明了。
我们在项目中会看到有一些库后面添加了incompatible标记:
github.com/dgrijalva/jwt-go v3.2.0+incompatible
jwt-go这个库就是这样的,这是因为jwt-go的版本已经大于2了,但是他们的module path仍然没有添加v2、v3这样的后缀,不符合Go的module管理规范,所以go module把他们标记为incompatible,不影响引用;
go module拉取依赖包本质也是go get行为,go get主要提供了以下命令:
命令 | 作用 |
---|---|
go get | 拉取依赖,会进行指定性拉取(更新),并不会更新所依赖的其它模块。 |
go get -u | 更新现有的依赖,会强制更新它所依赖的其它全部模块,不包括自身。 |
go get -u -t ./... | 更新所有直接依赖和间接依赖的模块版本,包括单元测试中用到的。 |
go get拉取依赖包取决于依赖包是否有发布的tags:
2 . 拉取的依赖包有发布tags
没有发布的tags:
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
v0.0.0:根据commit的base version生成的:
20190718012654:是这次提交的时间,格式是yyyyMMddhhmmss
fb15b899a751:是这个版本的commit id,通过这个可以确定这个库的特定的版本
github.com/beego/bee v1.12.0
replace用于解决一些错误的依赖库的引用或者调试依赖库;
场景举例:
举例1:
日常开发离不开第三方库,大部分场景都可以满足我们的需要,但是有些时候我们需要对依赖库做一些定制修改,依赖库修改后,我们想引起最小的改动,就可以使用replace命令进行重新引用,调试也可以使用replace进行替换,Go1.18引入了工作区的概念,调试可以使用work进行代替,后面会介绍;
举例2:
golang.org/x/crypto库一般我们下载不下来,可以使用replace引用到github.com/golang/crypto:
go mod edit -replace golang.org/x/crypto=github.com/golang/crypto@v0.0.0-20160511215533-1f3b11f56072
用于跳过某个依赖库的版本,使用场景一般是我们知道某个版本有bug或者不兼容,为了安全起可以使用exclude跳过此版本;
exclude (
go.etcd.io/etcd/client/v2 v2.305.0-rc.0
)
这个特性是在Go1.16版本中引入,用来声明该第三方模块的某些发行版本不能被其他模块使用;
使用场景:发生严重问题或者无意发布某些版本后,模块的维护者可以撤回该版本,支持撤回单个或多个版本;
这种场景以前的解决办法:
维护者删除有问题版本的tag,重新打一个新版本的tag;
使用者发现有问题的版本tag丢失,手动介入升级,并且不明真因;
引入retract后,维护者可以使用retract在go.mod中添加有问题的版本:
// 严重bug...
retract (
v0.1.0
v0.2.0
)
重新发布新版本后,在引用该依赖库的使用执行go list可以看到 版本和"严重bug..."的提醒;
该特性的主要目的是将问题更直观的反馈到开发者的手中;
go.sun文件也是在go mod init阶段创建,go.sum的介绍文档偏少,我们一般也很少关注go.sum文件,go.sum主要是记录了所有依赖的module的校验信息,内容如下:
image-20230102193717816
从上面我们可以看到主要是有两种形式:
其中module是依赖的路径,version是依赖的版本号。hash是以h1:
开头的字符串,hash 是 Go modules 将目标模块版本的 zip 文件开包后,针对所有包内文件依次进行 hash,然后再把它们的 hash 结果按照固定格式和算法组成总的 hash 值。
h1 hash 和 go.mod hash两者要不同时存在,要不就是只存在go.mod hash,当Go认为肯定用不到某个版本的时候就会省略它的h1 hash,就只有go.mod hash;
使用go modules的一个前置条件是Go语言版本大于等于Go1.11;
然后我们要检查环境变量GO111MODULE是否开启,执行go env
查看:
$ go env
GO111MODULE=off
执行如下命令打开go mod:
$ go env -w GO111MODULE=on
接下来我们随意创建一个项目:
$ mkdir -p asong/demo
$ cd asong/demo
执行go mod init初始化该项目:
$ go mod init github.com/asong/demo
go: creating new go.mod: module github.com/asong/demo
接下来我们在demo目录下创建main.go文件,写下如下代码:
package main
import (
"fmt"
cache "github.com/asong2020/go-localcache"
)
func main() {
c, err := cache.NewCache()
if err != nil {
return
}
key := "asong"
value := []byte("公众号:Golang梦工厂")
err = c.Set(key, value)
if err != nil {
return
}
entry, err := c.Get(key)
if err != nil {
return
}
fmt.Printf("get value is %s\n", string(entry))
err = c.Delete(key)
if err != nil {
return
}
}
然后执行go mod tidy命令:
image-20230102202521167
自动根据main.go文件更新依赖,我们再看一下go.mod文件:
image-20230102202600643
以上就是在项目对go.mod的简单使用;
工作区用来解决什么问题?
场景1:我们有时在本地会对一些三方依赖库进行特制修改,然后想在项目修改依赖库引用到本地进行调试,这时我们可以使用replace做替换,这样就可以在本地进行开发联调,这样虽然可以解决问题,但是会存在问题,因为是在项目的go.mod文件直接修改的,如果误传到远程仓库,会影响到其他开发同学;
场景2:我们在本地开发了一些依赖库,这时想在本地测试一下,还未发到远程仓库,那么我们在其他项目中引入该依赖库后,执行go mod tidy
就会报远程库没有找到的问题,所以就必须要把依赖库先推送到远程,在引用调试;
正是这些问题,Go语言在Go1.18正式增加了go work
工作区的概念,其实就是将N个Go Module组成一个Go Work,工作区的读取优先级是最高的,执行go help work
可以查看go work提供的功能:
$ go help work
Usage:
go work <command> [arguments]
The commands are:
edit edit go.work from tools or scripts
init initialize workspace file
sync sync workspace build list to modules
use add modules to workspace file
Use "go help work <command>" for more information about a command.
执行go work init
命令初始化一个新的工作区,在项目中生成一个go.work文件:
go 1.18
// 多模块添加
use (...)
replace XXXXX => XXXX v1.4.5
go.work文件与go.mod文件语法一致,go.work支持三个指令:
所以针对上述场景,我们使用go work init命令在项目中对本地依赖库进行关联即可解决,后续我们只需要在git配置文件中添加go.work文件不推送到远程即可;
我们也可以在编译时通过-workfile=off
指令禁用工作区模式:
$ go build -workfile=offf .
go.work的推出主要是用于在本地调试,不会因为修改go.mod引入问题;
现在大小公司的项目应该都已经在使用Go Modules
进行依赖包管理了,虽然Go Modules
相比于Maven、npm还不是很完善,但也在不断地进行优化,变得越来越好,如果你现在项目还没有使用go modules
,可以准备将项目迁移到go mod了,推荐你使用;
好啦,本文到这里就结束了,我们下期见。
6月5日,一张券商降薪截图在社交媒体疯传。截图提到,当日上午,某中字头头部券商召开大会,除了MD外全员降薪,且降薪不只是降奖金,而是直接降底薪。按照职级不同,SA1降6K,SA3降8K,VP降8K—10K。据了解,降薪大概率整体属实,但具体幅度有所差异,且不同区域、不同业务条线目前掌握的降薪情况也不尽相同。
今日,蔚来 CEO 李斌在 2023 高通汽车技术与合作峰会上爆料,蔚来第二代技术平台的全系车型已标配第三代骁龙座舱平台。
Meta公司周一(5月22日)推出了一个开源AI语言模型——大规模多语言语音(Massively Multilingual Speech, MMS)模型,可以识别和产生1000多种语言的语音——比目前可用的模型增加了10倍。研究人员表示,他们的模型可以转换1000多种语言,但能识别4000多种语言。
歌手孙燕姿在更新动态中回应了近日引发争议的“顶流AI歌手孙燕姿”,笑称粉丝已经接受她是“冷门”歌手,而AI成为了目前的顶流。
5月31日晚,荣耀方面对澎湃新闻记者表示,上海荣耀智能科技开发有限公司是荣耀位于上海的研究所,是荣耀在中国的5个研究中心之一,重点方向在终端侧核心软件、图形算法、通信、拍照等方面研究开发工作。荣耀强调,坚持以用户为中心,开放创新,与全球合作伙伴一起为用户提供最佳产品解决方案。
据北京市市场监督管理局公示信息,5月24日,苹果电子产品商贸(北京)有限公司因发布虚假广告被北京市东城区市场监督管理局处以20万元的行政处罚。
据外媒5月24日消息,全球最大的个人电脑制造商联想表示,在2023年1-3月期间,该公司裁员了约5%,这是由于PC市场不景气导致的。
日前,有网络博主号称拍摄到了小米首款汽车MS11的高清视频。从视频中可以看出,新车依旧包裹大面积的伪装,据该博主称,他之所以确定这是小米汽车,是因为靠近观察之后,发现它的三角形大灯轮廓和其最初手绘的小米汽车假想图几乎一模一样。
超过 350 名从事人工智能工作的高管、研究人员和工程师签署了这份由非盈利组织人工智能安全中心发布的公开信,认为人工智能具备可能导致人类灭绝的风险,应当将其视为与流行病和核战争同等的社会风险。
日前,以押注“颠覆性创新”著称的ARK Invest创始人Cathie Wood在接受媒体采访时表示,软件提供商将是人工智能狂潮的下一个受益板块。英伟达每卖出1美元的硬件,软件供应商SaaS供应商就会产生8美元的收入。
据报道,阿里巴巴研究员吴翰清已于近期离职,钉钉显示其离职时间是5月19日。在阿里内部,研究员的职级为P10。据消息人士透露,吴翰清离职后,选择AI短视频赛道创业,已经close一轮融资。对于上述消息,截至发稿,阿里尚未回应。
阿里巴巴集团官微宣布,2023年六大业务集团总计需新招15000人,其中校招超过3000人。同时表示,“近日,关于淘宝天猫、阿里云、菜鸟、本地生活各个业务裁员谣言传得很厉害,但谣言就是谣言。我们的招聘正在紧锣密鼓的进行。”
“现今每一个存在的应用都将被AI 2.0重构,我觉得整个AI大模型带来的机遇和技术浪潮,会比过去Windows和安卓大10倍。”李开复表示。
苹果发布Vision Pro头显,正式宣布开启空间计算时代;苹果还发布新款MacBook Air,新款Mac Studio,并展示了iOS17、iPadOS 17、macOS Sonoma和watchOS10等新系统;Vision Pro头显售价3499美元,将于2024年初正式在美国市场发售;华尔街并不看好Vision Pro,苹果股价周一创历史新高后由涨转跌。
5月25日,长城汽车就比亚迪秦PLUS DM-i、宋PLUS DM-i采用常压油箱,涉嫌整车蒸发污染物排放不达标的问题进行举报。
近日,一个名为“贾跃亭”的抖音账号悄然出现,带有“FF创始人、合伙人、首席产品及用户生态官, LeEco 乐视创始人”等标签,IP 地址显示为美国。
5月29日消息,继上周远超预期的财报业绩预测引得股价和市值史诗级暴涨后,今日,英伟达(NVIDIA)创始人兼CEO黄仁勋穿着标志性的皮衣,意气风发地出现在台北电脑展COMPUTEX 2023上,在主题演讲期间先是现场给自家显卡带货,然后一连公布涉及加速计算和人工智能(AI)的多项进展。
近日,苹果位于天猫的Apple Store官方旗舰店挂出直播预告,表示将在5月31日晚19时开启官方直播,这也是苹果官方在电商平台的全球首次直播。
前京东集团副总裁、京东探索研究院副院长梅涛自今年初离职后,确认在 AI 领域创业,成立生成式 AI 公司 HiDream.ai。