快速掌握 Go 工作区模式

大家好,我是煎鱼。

在 Go 项目的模块管理中,先是 GOPATH,然后到废弃。再到强推 Go modules,从被社区抗拒到 rsc 硬上弓。现在最新要了解的,就是工作区模式(workspace mode)。这是一个在 Go1.18 引入的重要特性。

之前一直没提过,今天补全这块的知识点。

背景

在 Go1.11 起有了 Go modules 后,看起来 Go 模块管理逐步按序有了约束、规范了起来。但也带来了一些使用上的问题。

现实开发时,当我们需要对多个关联模块进行开发(修改)时,这个事情就麻烦了起来。我见过两种方式。

1、第一种:直接在 go.mod 文件上配置 replace,配置到本地的开发目录。这是最常见的方式。

arduino 复制代码
// go.mod

replace example.com/golang/text => "../eddycjy/golang/text"

这种做法经常会有人不小心提交到 Git 仓库上。还挺折腾人的,一个不小心就为此 debug 了半天,或者发布部署一直卡着过不去。

2、第二种:直接在依赖模块上编码,编码到一定的程度。才上传 GitHub/GitLab。再去发布版本标签再引用。这种用法比较少,只有模块比较简单且对程序比较自信的会这么干。(不推荐)

总的来讲,就是有了 Go modules 后,多模块间的依赖开发还是挺麻烦的。要经常 replace,有时候又会忘了删。

go work 指令集

在大家痛苦了许久后,Go1.18 时终于发布了工作区模式的方式,来优化这个用法和问题。

以下是 go work 的指令集:

go 复制代码
go work <command> [arguments]
  • edit:从工具或脚本中编辑 go.work。
  • init:初始化工作区文件(go.work)。
  • sync:将工作区构建列表同步到模块。
  • use:将模块添加到工作区文件。

快速使用

接下来我们快速应用 Go 工作区模式,让大家有个直观的了解。需要注意,该特性需要确保 Go 版本 >= 1.18。

创建工作区

首先我们创建一个工作区,执行如下命令:

shell 复制代码
$ mkdir workspace-main && cd workspace-main 
$ go work init

执行完毕后会在该目录下创建一个 go.work 文件,文件内容包含:

go 复制代码
go 1.20

仅包含版本信息,因为当前是空白的工作区,只有初始化行为。

创建演示模块

go 复制代码
$ mkdir hello-world && cd hello-world
$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello

写入代码 hello.go:

go 复制代码
package main

import (
	"fmt"

	"golang.org/x/example/hello/reverse"
)

func main() {
	fmt.Println(reverse.String("Hello, 煎鱼"))
}

如果你这时候直接 go run。可能会出现如下报错:

lua 复制代码
hello.go:6:5: no required module provides package golang.org/x/example/hello/reverse: go.mod file not found in current directory or any parent directory; see 'go help modules'

看着非常迷惑人,很多同学以为是环境变量 GO111MODULE 没有设置为 on。其实是没有将本项目加入工作区中,导致运行错误。

所以可以看出来,在设计上是先有项目,再有工作区的路径。也是相对符合的。

这时候需要回到工作区目录 workspace-main。执行如下命令:

bash 复制代码
go work use ./hello-world

go.work 文件内会变成:

bash 复制代码
$ cat go.work 
go 1.20

use ./hello-world

再运行程序:

go 复制代码
$ go run hello-world/hello.go 
鱼煎 ,olleH

一切正常。

创建需修改的模块

这时候我们有了一个实际的诉求,我们希望 golang.org/x/example/hello 改一下这个 SDK 库。

如果是以前的话,我们需要写 replace 来解决。现在的话可以用工作区模式来完成这个诉求。

我们先需要回到工作区根目录 workspace-main 下,拉取这个 SDK 库到工作区中:

bash 复制代码
git clone https://go.googlesource.com/example

再将其引入项目的工作区中:

bash 复制代码
go work use ./example/hello

go.work 文件会变成:

bash 复制代码
go 1.20

use (
	./example/hello
	./hello-world
)

这里需要注意,go work 以 go.mod 为单位。如果你直接引入 ./example。是无法对 ./example/hello 的 module 起效果的。

在引入成功后,我们回到 ./example/hello 目录下的 reverse.go 文件,新增一个用于 Demo 的方法:

csharp 复制代码
...
func Hello() string {
	return "煎鱼,你好!"
}

再到 hello 项目中,新增调用:

go 复制代码
package main

import (
	"fmt"

	"golang.org/x/example/hello/reverse"
)

func main() {
	fmt.Println(reverse.String("Hello, 煎鱼"))
	fmt.Println(reverse.Hello())
}

输出结果:

鱼煎 ,olleH
煎鱼,你好

一切正常。满足不添加 replace 的要求,也使用了 go.work,不用担心把 replace 不小心提交到 Git 仓库中。

另外 Go 工作区中的项目在进行编译时,也是引用所配置好的工作区内的模块。而不是单单只针对开发阶段的 go run,也可以在产线上去使用,编译成二进制去应用和部署。

场景汇总

我们已经对 Go 的工作区模式有了一定的了解,其使用场景聚焦在如下:

  • 开发较大的产品,其项目存在着多个互相依赖的模块。可以直接设置成一个工作区。
  • 开发第三方库(类似 SDK 库),需要对上游的模块新增新特性。势必要在本地模块先引用做开发、测试、验证。也可以直接使用工作区。

总结

今天我们快速了解了 Go 工作区模式(workspace mode)的背景、使用、场景。这对于解决项目中多模块依赖有着一定的作用,可以不再需要去 go.mod 里 replace,算是给了一个规范化的解决方案。

但在实际应用中,我们会发现工作区模式的便利度,其实不太高。可能依赖模块数量少时,还不如 replace 一把梭来得快。

另外目前阶段的使用宣传还是做得比较弱的,前两天问了一圈,还真有一些同学不知道,也没有用过的。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

Go 图书系列

推荐阅读

相关推荐
李歘歘1 天前
Golang笔记——切片与数组
开发语言·笔记·后端·golang·go
童先生3 天前
GoLand 如何集成 Netty?
go
卷心菜是俺3 天前
并发服务器框架——zinx
运维·服务器·go·github·代理模式
探索云原生3 天前
基于 Admission Webhook 实现 Pod DNSConfig 自动注入
云原生·kubernetes·go·dns
Pandaconda4 天前
【Golang 面试题】每日 3 题(二十二)
开发语言·笔记·后端·面试·golang·go·channel
Pandaconda4 天前
【Golang 面试题】每日 3 题(二十三)
开发语言·后端·面试·golang·go·channel
寻找优秀的自己4 天前
WebSocket 实现指南
websocket·网络协议·go
Pandaconda5 天前
【Golang 面试题】每日 3 题(二十一)
开发语言·笔记·后端·面试·职场和发展·golang·go
菠菠萝宝5 天前
【Go学习】-01-1-入门及变量常量指针
开发语言·学习·golang·go·软件工程·web·go1.19
我是前端小学生6 天前
Go 语言中的 Context:掌控并发,优雅退出
go