Go 项目的布局:你是怎么组织你的Go代码的?
ztj100 2025-07-23 19:25 51 浏览 0 评论
点击上方蓝色“Go语言中文网”关注我们,领全套Go资料,每天学习 Go 语言
本文还是基于 GOPATH,这块忽略,其他部分可以借鉴
读过了 `Tour of Go`[1],在
https://play.studygolang.com/[2] 上把玩过,然后你感觉你准备好写一些代码了。很棒!但是,你不确定该如何组织你的项目。可以将代码放在你想放的任意地方吗?有没有组织代码的标准方式?如果想有多个应用程序的二进制文件呢?“go getable” 是指什么?你可能会问自己这些问题。
首先,你必须了解 Go 的工作空间。`How to Write Go Code`[3] 是个很好的起点。缺省地,Go 将所有代码保管在同一个工作空间,并期望所有代码都在同一个工作空间。这个地方由环境变量 GOPATH 来标识。对你来说这意味着什么?意味着你必须将代码放在默认的工作空间或者必须修改 GOPATH 环境变量,指向你自己的代码位置。不管哪种方式,项目的真正源代码都需要放在 src 子目录下(即 $GOPATH/src/your_project 或 $
GOPATH/src/github.com/your_github_username/your_project)。技术上讲,如果你无需导入外部包且使用相对路径导入自己的代码,你的工程不一定非要放在工作空间里,但不推荐这样做。不过玩具项目或概念验证(Poc)项目这么做是可以的。Go 1.1 确实引入了模块的概念,允许你将项目代码放在 GOPATH 之外,且不受上述的导入限制,但直到现在这还是一个实验性的功能。
你已经将你的项目目录放在正确的地方。接下来呢?
对于你是唯一开发者的概念验证(Poc)项目或特别小的项目,将项目代码都写在根目录下的 main.go 里就够了。如果知道你的项目将会变得足够大或者它会上生产环境,而且其他人会贡献代码,那你就应该考虑至少采用这里罗列的项目布局样式中的一些。
有一些项目布局样式在 Go 生态系统中脱颖而出。cmd 和 pkg 目录是最常见的两个样式。你应当采用这些样式,除非你的项目特别小。
cmd 布局样式在你需要有多个应用程序二进制文件时十分有用。每个二进制文件拥有一个子目录(即 your_project/cmd/your_app)。这个样式帮助保持你的项目下的包(project/package) ‘go gettable’。什么意思?这意味着你可以使用 go get 命令拉取(并安装)你的项目,项目的应用程序以及库(比如,go get
github.com/your_github_username/your_project/cmd/appxg)。你不必非要拆分应用程序文件,通过设置正确的 go build 标记你可以构建每个应用程序,但是由于不知道该构建哪个应用程序, go get 就无法正常工作了。官方的 Go tools[4] 是 cmd 布局样式的一个例子。很多知名的项目也使用了同样的样式:Kubernetes[5], Docker[6], Prometheus[7], Influxdb[8]。
pkg 布局样式也十分受欢迎。对新手 Go 开发者来讲这是最容易混淆的一个包结构概念,因为 Go 的工作空间就有一个同名的目录但那个目录有不同的用途(用来存储 Go 编译器构建的包的 object 文件)。pkg 目录是放置公共库的地方。它们可以被你的应用内部使用。也可供外部项目使用。这是你和你代码的外部使用者之间的非正式协定。其它项目会导入这些库并期望它们正常工作,所以在把东西放到这里前请三思。很多知名的项目使用了这个样式:Kubernetes[9], Docker[10], Grafana[11], Influxdb[12], Etcd[13].
pkg 目录下的某些库并不总是为了公共使用。为什么呢?因为很多现有的 Go 项目诞生在能隐藏内部包之前。一些项目将内部库放在 pkg 目录下,以便保持与其它部分代码结构的一致。另外一些项目将内部库放置在 pkg 目录之外另外的目录里。Go 1.4[14] 引入了使用 internal 隐藏内部库的能力。什么意思呢?如果你将代码放在 ‘internal’目录,外部项目则无法导入那些代码。即使是项目内部的其它代码,如果不在 internal 目录的父目录里,也无法访问这些内部代码。这个功能使用还不广泛因为它相对较新;但是作为一个额外(在 Go 用大小写区分函数可见性的规则之外)的控制层它有极大价值。很多知名的项目使用了这个样式:Dep[15], Docker[16], Nsq[17], Go Ethereal[18], Contour[19]。
internal 目录是放置私有包的地方。你可以选择性地添加额外的结构来分离内部共享的库(比如,
your_project/internal/pkg/your_private_lib)以及不希望别人导入的应用程序代码(比如,
your_project/internal/app/your_app)。当你将全部私有代码都放在 ‘internal’ 目录,cmd 目录下的应用程序就可以被约束成一些小文件,其只需定义对应于应用程序二进制文件的 ‘main’ 函数。其余代码都从 internal 或 pkg 目录导入(Heptio 中的 ark[20],以及 Grafana 中的 loki[21],是这个 微型 main 函数 包样式的好例子)。
如果你 fork 并修改了外部项目的一块该如何?有些项目将这些代码放在 pkg 目录下,但更好的做法是将它放在顶层目录下的 third_party 目录,以便将你自己的代码和你从别人那里借用的代码区分开来。
你在项目里导入的外部包呢?它们去哪里?你有几个选项。你可以将它们放在项目以外。使用 go get 安装的包将保存在你的 Go 工作空间。大部分情况下可以正常工作,但视具体包而定,它可能会变得脆弱和不可预测,因为别人在构建你的项目时他们可能会拿到这个包的一个不向后兼容的版本。解决办法是 ‘vendoring’。使用 ‘vendoring’ 你通过将依赖与项目一起提交来将它们固定。Go 1.6[22] 导入了一种标准的方式来 ‘vendor’ 外部包(在 Go 1.5 中是实验性功能)。将外部包放在 vendor 目录。这与 third_party 目录有何区别呢?如果你导入了外部代码且原样使用它就放在 vendor 目录。如果你使用的是修改版的外部包就放在 third_party 目录。
如果你想学习更多关于其他 Go 项目使用的项目结构请阅读 ‘Analysis of the Top 1000 Go Repositories’[23]。它有点陈旧,不过依然有用。
一个真正的项目也会有另外的目录。你可以使用这个布局模版作为你的 Go 项目的起点:
https://github.com/golang-standards/project-layout[24]。它涵盖了这篇博客里描述的 Go 项目布局样式并包括很多你需要的支持目录。
现在是时候写些代码了!如果你还没安装 Go 请查看这个 quick setup guide for Mac OS X[25] (其他平台的安装也是类似的)。如果还没浏览过请你浏览 ‘Tour of Go’[26] ,然后读一下 ’50 Shades of Go’[27] 去了解 Go 中最常见的坑,这会在你开始写代码和调试代码时节省很多时间。
- Golang[28]
- Go[29]
- Standards[30]
- Project Structure[31]
via: https://medium.com/golang-learn/go-project-layout-e5213cdcfaa2
作者:Kyle C. Quest[32]译者:krystollia[33]校对:DingdingZhou[34]
本文由 GCTT[35] 原创编译,Go 中文网[36] 荣誉推出
参考资料
[1]
Tour of Go: https:/tour.studygolang.com
[2]
https://play.studygolang.com/: https://play.studygolang.com/
[3]
How to Write Go Code: https://golang.org/doc/code.html
[4]
Go tools: https://github.com/golang/tools/tree/master/cmd
[5]
Kubernetes: https://github.com/kubernetes/kubernetes/tree/master/cmd
[6]
Docker: https://github.com/moby/moby/tree/master/cmd
[7]
Prometheus: https://github.com/prometheus/prometheus/tree/master/cmd
[8]
Influxdb: https://github.com/influxdata/influxdb/tree/master/cmd
[9]
Kubernetes: https://github.com/kubernetes/kubernetes/tree/master/pkg
[10]
Docker: https://github.com/moby/moby/tree/master/pkg
[11]
Grafana: https://github.com/grafana/grafana/tree/master/pkg
[12]
Influxdb: https://github.com/influxdata/influxdb/tree/master/pkg
[13]
Etcd: https://github.com/coreos/etcd/tree/master/pkg
[14]
Go 1.4: https://golang.org/doc/go1.4
[15]
Dep: https://github.com/golang/dep/tree/master/internal
[16]
Docker: https://github.com/moby/moby/tree/master/internal
[17]
Nsq: https://github.com/nsqio/nsq/tree/master/internal
[18]
Go Ethereal: https://github.com/ethereum/go-ethereum/tree/master/internal
[19]
Contour: https://github.com/heptio/contour/tree/master/internal
[20]
ark: https://github.com/heptio/ark/blob/master/cmd/ark/main.go
[21]
loki: https://github.com/grafana/loki/blob/master/cmd/loki/main.go
[22]
Go 1.6: https://golang.org/doc/go1.6
[23]
‘Analysis of the Top 1000 Go Repositories’: http://blog.sgmansfield.com/2016/01/an-analysis-of-the-top-1000-go-repositories/
[24]
https://github.com/golang-standards/project-layout: https://github.com/golang-standards/project-layout
[25]
quick setup guide for Mac OS X: https://medium.com/golang-learn/quick-go-setup-guide-on-mac-os-x-956b327222b8
[26]
‘Tour of Go’: https://tour.golang.org/
[27]
’50 Shades of Go’: https://tour.golang.org/
[28]
Golang: https://medium.com/tag/golang
[29]
Go: https://medium.com/tag/go
[30]
Standards: https://medium.com/tag/standards
[31]
Project Structure: https://medium.com/tag/project-structure
[32]
Kyle C. Quest: https://medium.com/@CloudImmunity
[33]
krystollia: https://github.com/krystollia
[34]
DingdingZhou: https://github.com/DingdingZhou
[35]
GCTT: https://github.com/studygolang/GCTT
[36]
Go 中文网: https://studygolang.com/
相关推荐
- Linux集群自动化监控系统Zabbix集群搭建到实战
-
自动化监控系统...
- systemd是什么如何使用_systemd/system
-
systemd是什么如何使用简介Systemd是一个在现代Linux发行版中广泛使用的系统和服务管理器。它负责启动系统并管理系统中运行的服务和进程。使用管理服务systemd可以用来启动、停止、...
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
-
Linux系统日常巡检脚本,巡检内容包含了,磁盘,...
- 7,MySQL管理员用户管理_mysql 管理员用户
-
一、首次设置密码1.初始化时设置(推荐)mysqld--initialize--user=mysql--datadir=/data/3306/data--basedir=/usr/local...
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
-
1.1数据库的核心概念在开始Python数据库编程之前,我们需要先理解几个核心概念。数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它就像一个电子化的文件柜,能让我们高效...
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
-
设置WGCloud开机自动启动服务init.d目录下新建脚本在/etc/rc.d/init.d新建启动脚本wgcloudstart.sh,内容如下...
- linux系统启动流程和服务管理,带你进去系统的世界
-
Linux启动流程Rhel6启动过程:开机自检bios-->MBR引导-->GRUB菜单-->加载内核-->init进程初始化Rhel7启动过程:开机自检BIOS-->M...
- CentOS7系统如何修改主机名_centos更改主机名称
-
请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习1.前言本文将讲解CentOS7系统如何修改主机名。...
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
-
在Linux服务器管理中,SSH(SecureShell)是远程操作的核心工具。以下是SSH终端操作的常用命令和技巧,涵盖连接、文件操作、系统管理等场景:一、SSH连接服务器1.基本连接...
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
-
为什么需要配置开机自启?想象一下:电商服务器重启后,MySQL和Nginx没自动启动,整个网站瘫痪!这就是为什么开机自启是Linux运维的必备技能。自启服务能确保核心程序在系统启动时自动运行,避免人工...
- Kubernetes 高可用(HA)集群部署指南
-
Kubernetes高可用(HA)集群部署指南本指南涵盖从概念理解、架构选择,到kubeadm高可用部署、生产优化、监控备份和运维的全流程,适用于希望搭建稳定、生产级Kubernetes集群...
- Linux项目开发,你必须了解Systemd服务!
-
1.Systemd简介...
- Linux系统systemd服务管理工具使用技巧
-
简介:在Linux系统里,systemd就像是所有进程的“源头”,它可是系统中PID值为1的进程哟。systemd其实是一堆工具的组合,它的作用可不止是启动操作系统这么简单,像后台服务...
- Linux下NetworkManager和network的和平共处
-
简介我们在使用CentoOS系统时偶尔会遇到配置都正确但network启动不了的问题,这问题经常是由NetworkManager引起的,关闭NetworkManage并取消开机启动network就能正...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
Linux下NetworkManager和network的和平共处
-
Kubernetes 高可用(HA)集群部署指南
-
linux系统启动流程和服务管理,带你进去系统的世界
-
7,MySQL管理员用户管理_mysql 管理员用户
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
- 最近发表
-
- Linux集群自动化监控系统Zabbix集群搭建到实战
- systemd是什么如何使用_systemd/system
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
- 7,MySQL管理员用户管理_mysql 管理员用户
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
- linux系统启动流程和服务管理,带你进去系统的世界
- CentOS7系统如何修改主机名_centos更改主机名称
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)