文章

模块与包管理

模块与包管理

核心概念

概念说明类比
ModuleGo 的依赖管理单元,由 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.modgo 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 pathv1.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 replacego.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