接下来我们将从一个真实的 Go 项目开始,逐步开启 Go 的学习之旅。
学习一个新的编程语言,第一步通常不是马上去看语法,而是先把开发环境和项目创建起来。只有能够亲手运行代码,后面的学习才会更加直观。
Go 版本的选择
我们先看一下当前安装的 Go 版本:
go version |
输出类似:
go version go1.21.13 darwin/arm64 |
选择 Go 版本时,需要区分两种情况。
新项目选择 Go 版本
如果是一个全新的项目,通常选择 Go 官方当前的稳定版本即可。Go 官方下载页面会提供各个平台的安装包,目前最新稳定版本可以在官方发布页面查看。
但是也不能只看 Go 版本本身,还需要看项目依赖的框架要求。例如当前 Gin 的最新版本要求 Go 1.25 或更高版本,那么使用 Gin 最新版本时,Go 版本也不能太老。
一般可以按照下面的顺序选择:
- 优先选择 Go 的稳定版本,不使用实验版本。
- 查看项目依赖的最低 Go 版本。
- 查看公司项目、部署环境和 CI 使用的 Go 版本。
- 在开发、测试和生产环境中尽量保持 Go 的大版本一致。
学习已有项目时选择 Go 版本
这次我们学习的是一个真实的 Go 项目。打开项目根目录下的 go.mod 文件,可以看到:
module example.com/internal-service |
这里的 go 1.21 是这个模块声明的 Go 版本。它告诉我们这个项目至少按照 Go 1.21 的模块和语言规则来构建。
因此,在学习这个项目时,我们先以 Go 1.21 为基线,不要一开始就修改项目的 Go 版本。版本升级不仅可能影响语法,还可能影响依赖版本、测试结果和构建环境。
这也是实际开发中经常遇到的情况:
- 新项目可以选择当前稳定版本。
- 已有项目优先遵循项目声明的版本。
- 如果要升级版本,需要同时检查依赖、测试、构建和部署环境。
创建第一个 Go 项目
我们先创建一个单独的学习目录,不直接修改原项目:
mkdir go-learning |
使用 go mod init 创建 Go 模块:
go mod init github.com/redye/go-learning |
执行完成后,目录下会生成一个 go.mod 文件:
module github.com/redye/go-learning |
模块名称以后会作为其他 Go 代码导入当前项目时使用的路径。
如果项目只是公司内部使用,也可以使用公司的模块路径。例如原项目使用的是类似下面的内部模块名:
module example.com/internal-service |
如果项目以后要发布到 GitHub,模块路径通常应该和代码仓库地址保持一致。
需要选择什么技术框架
Go 本身已经提供了 net/http 标准库,可以直接编写 HTTP 服务,不一定需要额外的 Web 框架。
例如,使用标准库可以这样启动一个服务:
package main |
标准库的优点是依赖少,适合学习 HTTP 服务的基本原理。
但是,我们这次要学习的真实项目是一个面向 App 的 HTTP 接口服务,项目中使用了 Gin:
require ( |
Gin 提供了路由、路由分组、中间件、参数绑定、JSON 响应和异常恢复等能力,比较适合 REST API 和 BFF 服务。这个项目中的路由、中间件和请求处理,也都是围绕 Gin 的 gin.Engine 和 gin.Context 展开的。
所以这次的选择是:
- 先了解 Go 标准库,知道 HTTP 服务是怎么工作的。
- 实际项目练习使用 Gin,逐步理解框架如何封装标准库。
- 暂时不额外引入数据库 ORM、日志框架等依赖,真正用到时再安装。
安装 Gin 依赖
创建模块后,我们安装和目标项目保持一致的 Gin 版本:
go get github.com/gin-gonic/gin@v1.10.1 |
安装完成后,go.mod 会增加依赖:
require github.com/gin-gonic/gin v1.10.1 |
同时通常会生成或更新 go.sum。这个文件记录依赖模块的校验信息,用来保证每次下载到的依赖内容是一致的。
Go 官方推荐使用 Go Modules 管理依赖。模块的核心文件有两个:
go.mod:记录模块名称、Go 版本和依赖版本。go.sum:记录依赖包的校验值。
它们都应该提交到 Git 仓库中。
编写第一个 Gin 程序
编写第一个 Gin 程序
在当前目录创建 main.go:
package main |
这里有几个新的内容:
gin.Default()创建一个带有日志和异常恢复中间件的路由器。r.GET("/hello", ...)注册一个 GET 请求。*gin.Context表示当前 HTTP 请求的上下文。c.JSON将数据以 JSON 格式返回给客户端。gin.H本质上是一个方便构造 JSON 对象的 map。
运行程序:
go run . |
打开另一个终端发送请求:
curl http://localhost:8080/hello |
可以看到返回结果:
{"message":"Hello, Go"} |
到这里,一个最简单的 Go Web 项目就运行起来了。
macOS 下的 missing LC_UUID 错误
如果在较新的 macOS 上运行较旧 Go 版本编译的程序,可能会遇到下面的错误:
dyld: missing LC_UUID load command |
这是因为旧版 Go 的内部链接器生成的 macOS 可执行文件可能没有 LC_UUID,而系统加载程序 dyld 需要这个信息。程序可能已经编译成功,但在启动时被系统拒绝加载。
如果暂时不能升级 Go,可以尝试让 Go 使用 macOS 的外部链接器运行程序:
CGO_ENABLED=1 go run -ldflags="-linkmode=external" . |
这种方式需要安装 Xcode Command Line Tools,其中包含 clang。可以用下面的命令检查:
xcode-select -p |
如果提示找不到 C 编译器,可以先安装 Command Line Tools:
xcode-select --install |
外部链接可以作为临时绕过方式;条件允许时,升级到较新的 Go 版本会省去这项额外参数。
在 GoLand 中配置
如果平时通过 GoLand 运行程序,可以把相同的参数添加到运行配置中:
打开
Run→Edit Configurations...,选择当前的 Go 运行配置。在
Go tool arguments中填写:-ldflags=-linkmode=external
在
Environment variables中添加CGO_ENABLED=1。保存配置后重新运行程序。
注意,参数要填写在 Go tool arguments 中,而不是 Program arguments。前者传给 Go 工具,后者会作为参数传给编译后的程序。
升级 Go 版本
外部链接是一种临时绕过方式。Go 1.24 起,Go 链接器默认会在 macOS 可执行文件中生成 LC_UUID。如果可以升级 Go,可以安装 Go 1.24 或更新版本:
如果当前 Go 是通过 Homebrew 安装的,可以运行:
brew update |
也可以从 Go 官方下载页面下载安装包。安装后重新打开终端或 IDE,并检查当前实际使用的 Go 版本:
which go |
如果电脑上安装了多个 Go 版本,which go 可以帮助确认命令行当前找到的是哪一个。升级本机工具链不代表必须同步修改 go.mod 中的 go 指令;是否调整该版本应结合项目要求和依赖情况决定。
Go Modules 常用命令
添加或升级依赖
使用 go get 添加依赖或指定依赖版本:
go get github.com/gin-gonic/gin@v1.10.1 |
升级到指定版本:
go get github.com/gin-gonic/gin@v1.10.2 |
不要为了“更新一下依赖”就直接对所有依赖执行升级。大型项目中,一个依赖的升级可能会同时升级它的间接依赖,应该先查看变更再进行测试。
整理依赖
go mod tidy |
go mod tidy 会根据项目源码和测试代码检查依赖:
- 添加缺少的依赖。
- 删除没有使用的依赖。
- 更新
go.sum。 - 保持
go.mod和实际代码一致。
通常在新增、删除或调整 import 后执行一次。
下载依赖
go mod download |
这个命令会提前把模块下载到本地缓存,但不会替我们判断项目到底使用了哪些依赖。整理依赖主要使用 go mod tidy,提前下载依赖才使用 go mod download。
查看依赖
查看当前模块的直接依赖:
go list -m |
查看完整的模块依赖关系:
go list -m all |
查看某个依赖为什么会被引入:
go mod why github.com/gin-gonic/gin |
私有依赖和目标项目
真实项目除了 Gin、Apollo 等公开依赖,还使用了公司内部的 GitLab 模块,例如:
example.com/company/go-agent |
这类依赖通常需要登录权限,不能像公开模块一样直接从公共代理下载。公司项目一般需要配置 GOPRIVATE,让 Go 知道哪些模块属于私有模块:
go env -w GOPRIVATE=example.com/company/* |
具体的 GitLab 访问方式还要遵循公司的开发环境和凭证配置。学习阶段的公开依赖不需要配置这一项。
小结
这一篇我们完成了一个 Go 项目的最小闭环:
- 查看并选择 Go 版本。
- 使用
go mod init创建模块。 - 选择标准库或 Gin Web 框架。
- 使用
go get安装固定版本的 Gin。 - 使用
go.mod和go.sum管理依赖。 - 使用
go run .启动第一个 HTTP 服务。
下一篇开始学习 main.go 文件,从 package main、import、函数和 gin.Context 开始,逐步理解这个程序是如何运行起来的。