模块与包管理
核心概念
| 概念 | 说明 | 类比 |
|---|
| Module | Go 的依赖管理单元,由 go.mod 定义 | 相当于 Python 的 package / Node 的 package.json |
| Package | 同一目录下的一组 .go 文件 | 相当于 Python 的 module |
| Import Path | 包的唯一标识路径 | 相当于 Java 的全限定类名 |
| Version | 语义化版本号 vMAJOR.MINOR.PATCH | 标准 SemVer |
go.mod 文件结构
module github.com/qingsongchou/care-mate // 模块路径
go 1.22 // Go 语言版本
require (
github.com/go-chi/chi/v5 v5.0.12 // 直接依赖
github.com/skywalking-go v0.6.0
gorm.io/gorm v1.25.10
)
require (
github.com/jinzhu/inflection v1.0.0 // indirect // 间接依赖
)
replace (
// 本地替换(开发调试常用)
github.com/qingsongchou/care-mate/pkg/auth => ./pkg/auth
// 代理替换(国内常用)
golang.org/x/net => golang.org/x/net v0.23.0
)
exclude (
// 排除有已知 bug 的版本
github.com/some/pkg v1.2.3
)
retract (
// 撤回自己发布的有问题的版本
[v1.1.0, v1.1.5]
)
常用命令速查
| 命令 | 作用 | 示例 |
|---|
go mod init | 初始化新模块 | go mod init github.com/user/project |
go mod tidy | 整理依赖(添加缺失、移除多余) | go mod tidy |
go mod download | 下载依赖到本地缓存 | go mod download |
go mod verify | 验证依赖完整性 | go mod verify |
go mod why | 解释为什么需要某个依赖 | go mod why -m gorm.io/gorm |
go mod graph | 打印依赖图 | go mod graph |
go mod edit | 编程式编辑 go.mod | go mod edit -go=1.22 |
go get | 添加/升级依赖 | go get gorm.io/gorm@v1.25.10 |
go get -u | 升级到最新次版本 | go get -u gorm.io/gorm |
go get -u ./... | 升级所有依赖 | go get -u ./... |
go get -u=patch | 仅升级补丁版本 | go get -u=patch ./... |
版本选择算法 MVS
Go 使用 Minimum Version Selection(最小版本选择) 算法:
假设:
模块 A 依赖 B v1.2.0
模块 C 依赖 B v1.3.0
MVS 结果:选择 B v1.3.0(满足所有要求的最小版本)
对比 npm/yarn 的策略:可能选择 B v1.5.0(最新兼容版本)
MVS 的核心优势:可复现构建——只要 go.mod 不变,任何人 go build 都得到相同结果。
语义化版本规则
| 变更类型 | 版本号变化 | go.mod 影响 | 示例 |
|---|
| Bug 修复 | PATCH+1 | 自动兼容 | v1.2.3 → v1.2.4 |
| 新功能 | MINOR+1 | 自动兼容 | v1.2.3 → v1.3.0 |
| 破坏性变更 | MAJOR+1 | 需改 import path | v1.2.3 → v2.0.0 |
关键规则:Go 的 v2+ 版本必须在 import path 中加版本后缀
import "github.com/user/pkg/v2" // v2 版本的导入路径
go.sum 文件
go.sum 记录每个依赖的哈希校验值,确保依赖内容不被篡改:
gorm.io/gorm v1.25.10 h1:abcd1234... // 模块 zip 哈希
gorm.io/gorm v1.25.10/go.mod h1:efgh5678... // go.mod 哈希
| 场景 | 操作 |
|---|
| go.sum 缺失条目 | go mod tidy 补全 |
| 哈希校验失败 | 清缓存 go clean -modcache 后重新下载 |
| CI 中必须提交 go.sum | 确保可复现构建 |
国内代理配置
# 设置 GOPROXY(七牛云镜像)
go env -w GOPROXY=https://goproxy.cn,direct
# 设置 GOSUMDB(校验服务)
go env -w GOSUMDB=sum.golang.google.cn
# 私有仓库不走代理
go env -w GOPRIVATE=git.code.oa.com,github.com/qingsongchou
# GONOSUMDB:私有仓库不校验
go env -w GONOSUMDB=git.code.oa.com
包的可见性规则
// 大写开头 = 导出(public)
func ExportedFunc() {}
type ExportedType struct {
PublicField string // 外部可访问
privateField string // 仅包内可访问
}
// 小写开头 = 未导出(package-private)
func internalFunc() {}
type internalType struct {}
| 可见性 | 命名规则 | 访问范围 |
|---|
| 导出 | 首字母大写 | 所有导入方可访问 |
| 未导出 | 首字母小写 | 仅当前包内可访问 |
internal 包机制
project/
├── pkg/
│ └── internal/ // internal 目录
│ └── secret.go // 只能被 project/pkg/ 下的代码导入
├── other/
│ └── main.go // ❌ 不能导入 project/pkg/internal
internal/ 目录的代码只能被其父目录树内的代码导入,是 Go 控制包可见性的核心机制。
Workspace 模式(多模块开发)
# 场景:同时开发 care-mate 和共享库 care-common
# 初始化 workspace
go work init ./care-mate ./care-common
# 生成的 go.work 文件
# go 1.22
# use (
# ./care-mate
# ./care-common
# )
# 添加模块到 workspace
go work use ./new-module
# 移除模块
go work edit -dropuse=./old-module
| 对比 | go.mod replace | go.work |
|---|
| 影响范围 | 模块级 | 机器级(不提交) |
| 适用场景 | 正式依赖替换 | 本地多模块联调 |
| 是否提交 | 是 | 否(加入 .gitignore) |
| 优先级 | 低 | 高(workspace 覆盖 go.mod) |
版本管理最佳实践
# 打标签
git tag v1.0.0
git push origin v1.0.0
# 发布 v2(需要改 import path)
mkdir v2
# go.mod 中 module path 改为 .../v2
git tag v2.0.0
# 发布预发布版本
git tag v1.1.0-rc.1
# Go get 指定版本
go get github.com/user/pkg@v1.0.0
go get github.com/user/pkg@latest
go get github.com/user/pkg@commit-hash