前置知识
- 已安装 Go 1.11+
- 跑通过
go run . 跑通 Hello World - 知道
import 是什么
为什么需要 Go Module
2018 年之前,Go 项目的依赖管理一直被诟病:
- 第三方包没有版本概念,
go get 永远拉 master - 多个项目依赖同一包不同版本就崩
- 公司内部私有包必须放
$GOPATH/src 下
Go 1.11(2018-08) 引入 Go Module,通过 go.mod + go.sum 两个文件彻底解决:
- 语义化版本(
v1.2.3) - 内容哈希校验(防止供应链投毒)
- 跨项目工作区(多 module 并行开发)
- 私有仓库支持(GOPRIVATE / GONOSUMDB)
一、Go Module 基础
1.1 初始化
1
2
3
| mkdir myproject
cd myproject
go mod init github.com/yourname/myproject
|
生成 go.mod:
1
2
3
| module github.com/yourname/myproject
go 1.19
|
1.2 依赖管理命令
1
2
3
4
5
6
7
8
9
| go get github.com/gin-gonic/gin@v1.9.1 # 拉取指定版本
go get -u github.com/gin-gonic/gin # 升级到最新
go get -u=patch # 升级 patch 版本
go mod tidy # 清理无用 + 补全缺失
go mod download # 下载所有依赖到本地
go mod verify # 校验哈希
go mod vendor # 复制依赖到 ./vendor/
go mod graph # 打印模块依赖图
go mod why github.com/gin-gonic/gin # 解释为什么需要这个依赖
|
1.3 go.mod 文件结构
1
2
3
4
5
6
7
8
9
10
11
12
| module github.com/yourname/myproject
go 1.19
require (
github.com/gin-gonic/gin v1.9.1
github.com/spf13/viper v1.16.0
)
require github.com/stretchr/testify v1.8.4 // indirect
replace github.com/old/pkg => github.com/new/pkg v1.0.0
|
module:当前模块路径go:最低 Go 版本要求require:直接依赖// indirect:间接依赖(被其他 require 引入)replace:本地或 fork 替换exclude:排除某个版本
1.4 go.sum 是什么
go.sum 记录每个依赖的内容哈希(h1:xxxx):
1
2
| github.com/gin-gonic/gin v1.9.1 h1:9WCKbRy+nQzf2 instance=...
github.com/gin-gonic/gin v1.9.1/go.mod h1:7fiKi...
|
作用:团队协作时,CI 拉下来的依赖如果哈希不一致直接报错——防止中间人篡改。
二、多模块工作区(Go 1.18+)
痛点场景:你在同时开发两个模块 myapp 和 mylib,myapp 引用 mylib 的最新代码——mylib 还没发布,myapp 怎么调试?
2.1 旧方案:go.mod replace
1
2
3
| // myapp/go.mod
require github.com/yourname/mylib v0.0.0
replace github.com/yourname/mylib => ../mylib
|
缺点:每次 go mod tidy 都得手动维护 replace,切回线上版本时容易忘。
2.2 新方案:go.work(Go 1.18+)
1
2
3
4
5
6
7
8
| mkdir workspace
cd workspace
mkdir myapp && cd myapp && go mod init github.com/yourname/myapp
mkdir ../mylib && cd ../mylib && go mod init github.com/yourname/mylib
cd ..
go work init myapp mylib
|
生成 go.work:
1
2
3
4
5
6
| go 1.19
use (
./myapp
./mylib
)
|
效果:myapp 自动用 ./mylib 的最新代码,不用改 go.mod、replace 一行不需要。
go.work 文件不要提交到 Git(加到 .gitignore)——只在本地开发用。
三、典型工程化目录结构
1
2
3
4
5
6
7
8
9
10
11
12
13
| ---------------------------------- go.mod 模块导入
---------------------------------- config 配置文件
---------------------------------- cmd 程序入口(可以有多个)
---------------------------------- ----- 程序1 main 入口
---------------------------------- ----- 程序2 main 入口
---------------------------------- doc 项目文档
---------------------------------- internal 内部包(外部无法 import)
---------------------------------- pkg 共享包(公开)
---------------------------------- public web 前端
---------------------------------- resources 静态资源
---------------------------------- scripts 脚本(部署、运维)
---------------------------------- swagger OpenAPI 文档
---------------------------------- test 集成测试
|
3.1 cmd 目录
每个可执行程序一个子目录:
1
2
3
4
5
| cmd/
├── server/
│ └── main.go # 编译出 bin/server
└── cli/
└── main.go # 编译出 bin/cli
|
1
2
3
4
5
6
7
8
9
10
| // cmd/server/main.go
package main
import (
"github.com/yourname/myapp/internal/server"
)
func main() {
server.Run()
}
|
3.2 internal 包
internal 目录下的包只能被同父目录的包引用——Go 编译器强制保护:
1
2
3
4
5
6
7
| myproject/
├── internal/
│ ├── dao/ # 数据访问
│ ├── logic/ # 业务逻辑
│ └── model/ # 数据模型
├── pkg/ # 公开包
│ └── utils/
|
外部项目 import "github.com/yourname/myapp/internal/dao" —— 编译失败。
3.3 pkg vs internal
| 目录 | 可见性 | 用途 |
|---|
pkg/ | 外部可引用 | 通用工具、SDK |
internal/ | 仅项目内部 | 业务实现、DAO |
3.4 go.mod 完整示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| module github.com/yourname/myapp
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/spf13/viper v1.16.0
github.com/go-sql-driver/mysql v1.7.1
gorm.io/gorm v1.25.5
go.uber.org/zap v1.26.0
)
require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
// ...
)
|
四、依赖版本管理最佳实践
4.1 选择版本策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 拉取最新稳定版
go get github.com/gin-gonic/gin@latest
# 拉取指定版本
go get github.com/gin-gonic/gin@v1.9.1
# 拉取 v2 大版本
go get github.com/gin-gonic/gin/v2@v2.0.0
# 升级到最新 minor
go get -u=minor github.com/gin-gonic/gin
# 升级到最新 patch
go get -u=patch github.com/gin-gonic/gin
|
4.2 升级所有依赖
1
2
3
| go get -u ./... # 升级所有直接依赖
go mod tidy # 重新整理
go test ./... # 跑测试确认没炸
|
4.3 降级依赖
1
2
3
| # 锁定到旧版本
go get github.com/gin-gonic/gin@v1.8.0
go mod tidy
|
4.4 私有仓库配置
1
2
3
4
5
| # 设置 GOPRIVATE,所有子路径不走代理
go env -w GOPRIVATE=github.com/yourcompany/*,gitlab.yourcompany.com/*
# 配置 SSH 拉取(推荐)
git config --global url."git@github.com:".insteadOf "https://github.com/"
|
4.5 镜像配置
1
2
3
4
5
| # 国内镜像
go env -w GOPROXY=https://goproxy.cn,direct
# 公司内部代理
go env -w GOPROXY=https://goproxy.yourcompany.com,https://goproxy.cn,direct
|
五、Go Module 常见问题
5.1 go.mod 文件中的 +incompatible
1
| require github.com/old/pkg v1.0.0+incompatible
|
表示该包未使用 Go Module(没有 go.mod),但有 v1.x.x tag。
5.2 依赖冲突
1
2
3
4
5
6
| # 查看冲突
go mod graph | grep gin
# 强制使用某个版本
go get github.com/gin-gonic/gin@v1.9.1
go mod tidy
|
5.3 vendor 目录
1
2
3
4
5
| # 把所有依赖复制到 ./vendor/
go mod vendor
# 编译时优先用 vendor
go build -mod=vendor
|
适用:离线环境、严格审计场景、Docker 构建不想走网络。
六、CI/CD 集成
6.1 GitHub Actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| name: build
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true
- run: go mod download
- run: go test ./...
- run: go build -o bin/server ./cmd/server
|
6.2 镜像构建
1
2
3
4
5
6
7
8
9
10
11
12
| # 多阶段构建
FROM golang:1.21-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/server ./cmd/server
FROM alpine:3.18
RUN apk --no-cache add ca-certificates tzdata
COPY --from=build /out/server /usr/local/bin/server
ENTRYPOINT ["server"]
|
最终镜像 ~15MB,scratch 基础镜像可压到 10MB 以下。
七、下一步
参考资料