当前位置: 首页 > news >正文

Go语言项目标准结构应该如何组织的?

这里写自定义目录标题

  • Go项目本身的目录结构
  • Go语言项目典型目录结构
    • GO语言项目最小标准目录结构
    • 可执行的Go语言项目目录结构
    • 库的Go语言项目目录结构
    • 关于`internal`目录
  • 总结
  • 参考文章

每当我们写一个非hello world实用程序的Go程序或库时,我们都会在项目结构、代码风格和标识符命名这三个“门槛”面前折腾很久,甚至得不到满意的答案。

在本文中,我们将详细介绍如何跨过Go项目结构的“门槛”,帮助您更快地进入Go语言的核心腹地,提高学习效率。

除非是像hello world这样简单的程序,否则每当我们写实用程序或库时,都会遇到使用什么项目结构的问题(通常一个Go项目对应一个repository)。在Go中,项目结构也很重要,因为它决定了项目内部包的布局和包依赖关系,也影响了外部项目对项目内包的依赖和引用。

Go项目本身的目录结构

我们来看看Go语言本身的项目结构,世界上第一个Go项目。

Go项目的项目结构从1.0版本发布开始就非常稳定,Go项目的顶层结构至今基本没有变化。截至go project commit 1e3ffb0c (2019.5.14),go项目结构如下。

$ tree -LF 1 ~/go/src/github.com/golang/go
./go
├── api/
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── doc/
├── favicon.ico
├── lib/
├── LICENSE
├── misc/
├── PATENTS
├── README.md
├── robots.txt
├── src/
└── test/

作为Go的“创始项目”,其项目结构布局对后续其他Go项目具有重要参考意义,尤其是早期Go项目中src目录下的结构,被Go社区广泛用作模板Go 应用程序项目结构。我们以早期Go 1.3版本的src目录下的结构为例。

$ tree -LF 1 ./src
./src
├── all.bash*
├── all.bat
├── all.rc*
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── lib9/
├── libbio/
├── liblink/
├── make.bash*
├── make.bat
├── Make.dist
├── make.rc*
├── nacltest.bash*
├── pkg/
├── race.bash*
├── race.bat
├── run.bash*
├── run.bat
├── run.rc*
└── sudo.bash*

关于上面src目录下面的结构,我总结了三个特点。

  1. 代码构建的脚本源文件放在src下的顶层目录下。

  2. src 下的二级目录 cmd 存放了 go 工具链相关的可执行文件(如:go、gofmt 等)及其主要包源文件的主目录。

$ tree -LF 1 ./cmd
./cmd
...
├── 6a/
├── 6c/
├── 6g/
...
├── cc/
├── cgo/
├── dist/
├── fix/
├── gc/
├── go/
├── gofmt/
├── ld/
├── nm/
├── objdump/
├── pack/
└── yacc/
  1. src下的二级目录,pkg,存放着上面cmd下各个工具链所依赖的packages、go runtime、go standard library的源文件。
$ tree -LF 1 ./pkg
./pkg
...
├── flag/
├── fmt/
├── go/
├── io/
├── log/
├── math/
...
├── syscall/
├── testing/
├── text/
├── time/
├── unicode/
└── unsafe/

从 Go 1.3 版本至今,Go 项目下的 src 目录发生了一些结构上的变化。

  • Go 1.4 从 Go 源代码树中的 src/pkg/xxx 中删除了 pkg 级别,而是直接使用 src/xxx。
  • Go 1.4 在 src 下增加了 internal 目录,用来存放不能外部导入的,只供 Go 项目使用的包。
  • Go 1.6 在 src 下增加了 vendor 目录,但是 Go 项目本身真正启用了 Go 1.7 中的 vendor 机制。vendor目录下存放着Go项目自身对外部项目的依赖,主要是golang.org/x下的包,包括:net、text、crypto等。该目录下的包随着每个Go版本的发布而更新。
  • Go 1.13版本在src下增加了go.mod和go.num,实现了go项目本身go module的迁移。go项目中的所有包都放在了std模块下,其依赖仍然是golang.org/x下的各种包
// Go 1.13版本go项目src下面的go.mod
module stdgo 1.12require (golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirectgolang.org/x/text v0.3.2 // indirect
)

这是最新 Go 1.16 版本的 src 目录中的完整布局。

├── Make.dist
├── README.vendor
├── all.bash*
├── all.bat
├── all.rc*
├── bootstrap.bash*
├── buildall.bash*
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── cmp.bash
├── go.mod
├── go.sum
├── internal/
├── make.bash*
├── make.bat
├── make.rc*
├── race.bash*
├── race.bat
├── run.bash*
├── run.bat
├── run.rc*
├── testdata/
...
└── vendor/

Go语言项目典型目录结构

GO语言项目最小标准目录结构

一个Go应用项目结构的标准布局是什么样子的,Go官方团队从来没有给出过一个参考标准。不过,作为Go语言项目的技术负责人, Russ Cox在开源项目的一期中给出了他对Go项目结构最小标准布局的思考。他认为Go项目的最低标准布局应该如下。

// 在Go项目仓库根路径下- go.mod
- LICENSE
- xx.go
- yy.go
...or- go.mod
- LICENSE
- package1- package1.go
- package2- package2.go
...

至于 pkg、cmd、docs,这些目录不应该是 Go 项目的标准结构的一部分,或者至少不需要。相信 Russ Cox 给出的最小标准布局与 Go 的“简单”哲学是一致的,足够灵活,可以满足各种 Go 项目的需求。

然而,在 Russ Cox 详细阐述最低标准之前,Go 社区处于“非标准”状态,早期 Go 语言自身的项目结构对现有大量 Go 开源项目的影响依然持久。对于更大的 Go 应用程序,我们势必会扩展“最小标准布局”。这个扩展显然不会盲目,但还是会参考Go语言项目自身的结构布局,所以我们有以下非官方标准建议的结构布局。

可执行的Go语言项目目录结构

基于Go语言项目本身的早期结构及其后续演变,Go社区经过多年的Go语言实践积累,逐渐形成了符合Russ Cox最小标准布局的典型项目结构,如下图所示。
在这里插入图片描述
以上是一个典型的支持构建二进制可执行文件的Go项目结构(在cmd下),我们分别看一下各个重要目录的用途。

  • cmd目录:存放项目要编译构建的可执行文件对应的主包源文件。如果有多个可执行文件要构建,则每个可执行文件的主包放在单独的子目录下,如图中的app1和app2;cmd目录下每个app的主包,将整个项目的依赖连接在一起;而且一般来说,主包应该是非常简洁的。我们会在main包中做一些命令行参数解析、资源初始化、日志工具初始化、数据库连接初始化等,之后我们会将程序的执行权交给更高级的执行控制对象;也有一些go项目把cmd的名字改成了app,但是它的实用性并没有改变。

  • pkg目录:项目本身要用到的,也是主包对应的可执行文件依赖的库文件;同时,该目录下的包也可以被外部项目引用,作为项​​目导出包的“聚合”;也有一些项目会 pkg 名称为 lib,但目录用途保持不变;因为Go语言项目在1.4版本去掉了pkg目录,所以有一些项目直接把包放在项目的根路径下,但是我觉得对于一些比较大的项目,包太多会成为顶层目录该项目不够简洁和“拥挤”。对于复杂的 Go 项目,我建议保留 pkg 目录。

  • Makefile:这里的Makefile是项目构建工具使用的脚本的“代表”,它可以代表任何第三方构建工具使用的脚本。对于大型项目来说似乎是必不可少的。在一个典型的Go项目中,项目构建工具的脚本通常放在项目的顶层目录下,比如这里的Makefile;对于构建脚本较多的项目,也可以创建构建目录,将构建脚本的规则属性文件和子构建脚本放入其中。

  • go.mod和go.sum:用于Go语言包依赖管理的配置文件。Go 1.11 引入了 go modules,go module 在 Go 1.16 成为了默认的依赖包管理和构建机制。所以对于新的 Go 项目,我们推荐使用 go modules 来进行包依赖管理。对于没有使用go modules进行包管理的项目(可能主要是使用go 1.11以前版本的go项目),可以换成dep的Gopkg.toml和Gopkg.lock或者glide的glide.yaml和glide.lock等.

  • vendor 目录(可选):vendor 是 Go 1.5 引入的机制,用于在项目本地缓存版本特定的依赖包,在引入 go modules 机制之前,可以实现基于 vendor 的可重现构建,以确保从相同构建的可执行文件源代码是等价的。go modules 本身可以在没有供应商的情况下实现可重现的构建。modules 本身可以在不需要 vendor 的情况下实现可重现的构建,但是 go modules 机制还保留了 vendor 目录(go mod vendor 在 vendor 下生成依赖项;go build -mod=vendor 启用基于 vendor 的构建),所以这里的 vendor 目录作为可选目录。一般我们只将vendor目录保留在项目的根目录下,否则会造成依赖选择上不必要的复杂性。

Go 1.11 引入了一个模块作为属于同一版本控制单元的包的集合。虽然 Go 支持项目/存储库中的多个模块,但这种管理方法可能会带来比一定比例的代码重复更多的复杂性。因此,如果项目结构中存在版本控制“分歧”,例如 app1 和 app2 版本并不总是同步,那么我建议将项目拆分为多个项目(存储库),每个项目都是一个单独的模块,用于单独的版本控制和演进.

库的Go语言项目目录结构

Go 1.4 发布时,Go 语言项目本身就去掉了 src 下的 pkg 层。这种结构变化对仅为库目的而构建的 Go 库类型项目的结构有一些影响。我们来看一个典型的 Go 库类型项目的结构,如下图所示。
在这里插入图片描述
我们看到库类型的项目结构也兼容 Go 项目的最低标准布局,但比旨在构建二进制可执行文件的 Go 项目更简单。

  • 删除了cmd和pkg子目录:由于该库只是一个组件库,因此无需保留存放二进制文件主要包源文件的cmd目录;由于 Go 库项目的初衷是将 API 暴露给外界(开源或内部组织),因此没有必要将其聚合在 pkg 目录下。
  • vendor不再可选:对于库类型的项目,我们不建议在项目中放置vendor目录来缓存库自身的第三方依赖;库项目应该只通过 go.mod(或其他包依赖管理工具的清单文件)明确说明项目依赖的模块或包以及版本要求。

关于internal目录

对于上述任何类型的Go项目,对于不想暴露给外部引用,而只供内部使用的包,可以通过Go 1.4引入的内部包机制来实现项目结构。对于库项目,最简单的方法是在顶层添加一个内部目录,将所有不想暴露给外部的包都放在该目录下,例如项目中的 ilib1 和 ilib2下面的结构。

// 带internal的Go库项目结构
$tree -F ./chapter2/sources/GoLibProj
GoLibProj
├── LICENSE
├── Makefile
├── README.md
├── go.mod
├── internal/
│   ├── ilib1/
│   └── ilib2/
├── lib.go
├── lib1/
│   └── lib1.go
└── lib2/└── lib2.go

这样,根据go内部机制的原理,内部目录的ilib1和ilib2可以被其他目录(如lib.go、lib1/lib1.go等)的代码导入使用,GoLibProj目录为根目录,但不是通过 GoLibProj 目录之外的代码。这允许选择性地公开 API 包。当然 internal 可以放在项目结构中的任何目录级别,但是项目结构设计者清楚什么要暴露给外部代码以及什么只能在兄弟目录或子目录中使用是至关重要的。

对于旨在构建二进制可执行类型的项目,我们还可以将不想暴露给外部的包聚合到项目顶层路径的internal下,呼应暴露给包的聚合目录pkg外部。

总结

这篇文章详细介绍了Go语言项目结构布局的历史和Go项目结构的事实标准。本文中构建二进制可执行文件类型和库类型的两个项目参考结构经过多年的实践被Go社区认可并广泛使用,并且兼容Russ Cox提出的Go项目最小标准布局,具有参考价值对于稍大的 Go 项目。然而,它们不是必需的,在 Go 语言的早期,将所有源文件放在位于项目根目录的根包中的做法在一些小型项目中同样有效。

对于旨在构建二进制可执行类型的项目,受 Go 1.4 项目结构的影响,删除 pkg 层次结构也是许多项目选择的结构布局。

上述参考项目结构类似于产品设计和开发领域的“最小可行产品”(mvp)的思想,开发者可以根据自己的实际需要在这样一个最小“项目结构核心”的基础上进行扩展。

在这里插入图片描述
By 极客Furzoom

参考文章

go project layout

相关文章:

Go语言项目标准结构应该如何组织的?

这里写自定义目录标题Go项目本身的目录结构Go语言项目典型目录结构GO语言项目最小标准目录结构可执行的Go语言项目目录结构库的Go语言项目目录结构关于internal目录总结参考文章每当我们写一个非hello world实用程序的Go程序或库时,我们都会在项目结构、代码风格和标…...

设计模式简介

设计模式简介 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错…...

#详细介绍!!! 线程池的拒绝策略(经典面试题)

本篇单独讲解线程池的拒绝策略,介绍了当线程池任务满了之后,线程池会以什么样的方式来响应添加进来的任务 目录 一:理解线程池拒绝策略的触发情况代码理解 二:线程池的四种常见的拒绝策略 1.ThreadPoolExecutor.AbortPolicy 2…...

正则表达式作业

利用正则表达式完成下面的操作: 一、不定项选择题 能够完全匹配字符串"(010)-62661617"和字符串"01062661617"的正则表达式包括(A ) A. r"\(?\d{3}\)?-?\d{8}" B. r"[0-9()-]" C. r"[0-9(-)]*\d*&qu…...

《扬帆优配》交易拥挤度达历史极值 当前A股TMT板块性价比几何?

上周,A股商场企稳,但盘面风格分歧再度加深:很多资金涌入以ChatGPT、数字经济为代表的TMT板块,而新能源以及前期强势的“中字头”种类都呈现了回调。兴业证券计算显现,3月24日,TMT及电子板块的商场成交金额占…...

C/C++开发,无可避免的IO输入/输出(篇三).字符串流(内存流)IO处理

目录 一、字符串流 1.1 字符串流继承体系 1.2 字符串流本质-类模板std::basic_stringstream 1.3 字符串流缓冲-std::stringbuf 1.4 stringbuf与序列缓冲 1.5 字符串流的打开模式 二、字符串流的运用 2.1 格式转换是其拿手好戏 2.2 字符串流仅提供移动赋值 2.3 std::basic_str…...

什么是HTTP请求?【JavaWeb技术】

HTTP请求是指从客户端到服务器的请求消息,建立HTTP请求需要经历以下7个步骤才能请求成功。 (1)建立TCP连接 在HTTP开始工作前,Web浏览器需先通过网络和Web服务器连接,连接过程主要使用TCP/IP完成。 (2)Web浏览器向Web服务器发送请求命令 一旦…...

浅聊面试这件事

目录 哪个时间点适合跳槽 如何准备面试 面试原则 面试常见问题 哪个时间点适合跳槽 金三银四、金九银十,这些都📌标记为我们的最佳跳槽节点,但是这些节点真的是最佳的么,也需要因人而异。 如果公司年前不发年终奖&#xff0c…...

【致敬未来的攻城狮计划】连续打卡第7天+瑞萨RA2E1点亮LED

开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯(http://yyds.recan-li.cn)和 瑞萨MCU (瑞萨电子 (Renesas Electronics Corporation) ) 联合发起的「 致敬未来的攻城狮计划 」的第 7 天,点击…...

Sam Altman专访:GPT-4没太让我惊讶,ChatGPT则让我喜出望外

导读ChatGPT、GPT-4 无疑是 2023 年年初人工智能界最大的「爆款」。3 月 26 日,OpenAI CEO、ChatGPT 之父 Sam Altman 接受了著名学者与科技播客、麻省理工大学研究员 Lex Fridman 的专访,Sam 分享了从OpenAI内部视角如何看待ChatGPT和GPT-4的里程碑式意…...

弯道超车的机会

弯道超车的机会 原文地址:https://bmft.tech/#/1-throught/0302-chance 前言 我一直很想把自己思考的东西表达出来,苦于语文成绩差,文字功力不够,想来想去也不知道用什么话来开场。我不喜欢站在高处对别人指指点点,…...

【设计模式】创建型模式之原型模式

【设计模式】创建型模式之原型模式 文章目录【设计模式】创建型模式之原型模式1.概述2. 构成3. 实现3.1 浅克隆3.2 深克隆1.概述 原型模式(Prototype Pattern):是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它…...

KMP算法——我欲修仙(功法篇)

个人主页:【😊个人主页】 系列专栏:【❤️我欲修仙】 学习名言:莫等闲、白了少年头,空悲切。——岳飞 系列文章目录 第一章 ❤️ 学习前的必知知识 第二章 ❤️ 二分查找 文章目录系列文章目录前言🚗&…...

【嵌入式Linux学习笔记】QT在Linux嵌入式设备上的使用

QT是目前主流的UI界面设计软件之一,Linux系统也支持QT应用,并且提供了很多方便的接口。所以有必要记录一下基于QT,在LCD屏幕上实现UI界面功能的各种细节。 学习视频地址:【正点原子】STM32MP157开发板 1. 系统配置 出于方便&am…...

js根据数据关键字实现模糊查询功能

js根据数据关键字实现模糊查询功能模糊查询实现模糊查询功能的步骤和一般方法第一步:创建假数据或请求接口数据第二步:分析数据格式,处理数据第三步:验证功能完整代码模糊查询 模糊查询功能是指在搜索或者查询时,允许…...

java获取对象属性

Field[] fields vo.getClass().getDeclaredFields(); for (Field field : fields) {//设置允许通过反射访问私有变量field.setAccessible(true);//获取字段的值String value "";Class<?> type field.getType();if (Date.class.equals(type)) {value DateU…...

51单片机(IIC协议OLED屏)

一、IIC协议 1、IIC协议概述 1.1、概述&#xff1a;IIC全称Inter-Integrated Circuit (集成电路总线) 是由PHILIPS公司在80年代开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。IIC属于半双 工同步通信方式 1.2、特点&#xff1a;简单性和有效性。 由于接口直…...

你知道,华为对项目经理要求的3项技能5项素质是什么吗?

很多人一定在好奇&#xff0c;华为对项目经理的要求是什么呢&#xff1f;普通项目经理应具备什么素质&#xff0c;才能进入华为这样的大厂&#xff0c;在严峻的经济形势下无惧裁员呢&#xff1f; 一、三项软技能 我们在华为举办的项目经理论坛中找到了答案&#xff1a;对于华…...

优漫动游 提升效率常用的C4D技巧

C4D是近几年非常热的趋势&#xff0c;经常有人问3D相关的问题&#xff0c;想把自己在找捷径的过程中觉得最实用的小技巧分享给大家   1、快速定位层级和模型   模型的过程中&#xff0c;经常遇到模型层级多难定位的问题&#xff0c;逐级打开或者全部展开对于定位模型使…...

基于蚁群算法的时间窗口路径优化

目录 背影 蚁群算法的原理及步骤 基本定义 编程思路 适应度函数 算法的规则 特点 主要参数 代码 结果分析 展望 背影 现代物流配送对时间要求更高,是否及时配送是配送是否成功的重要指标,本文对路径优化加时间窗口,实现基于蚁群算法的时间窗口路径优化, 蚁群算法 基本…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...