Files

191 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [go, module, proxy, dev-env]
create time: 2026-07-01 00:00
---
# Go Module 代理与依赖管理
## 概述
Go Modules 是 Go 自 v1.11 引入的官方依赖管理方案,GOPROXY 环境变量决定了模块下载的来源。国内用户通过配置代理加速拉取,本文深入 Module 体系与代理机制。
## 核心概念
### GOPROXY 的工作原理
```
go get ./...
│
▼
GOPROXY 列表依次查询 → 命中第一个返回
│ (类似 DNS 回退链)
▼
缓存到 ~/go/pkg/mod/cache/
│
▼
写入 go.sum(校验哈希)
```
```bash
# 默认值(中国大陆不可达)
go env GOFLAGS # 应设为空或不设
# 推荐:阿里 + goproxy.io 备用
go env -w GOPROXY=https://goproxy.cn,direct
# 也可以指定多个(逗号分隔,从左到右优先级递减)
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,https://goproxy.io,direct
```
为什么需要 `direct` 作为兜底?当模块是私有仓库、不在任何公开代理上时,`direct` 告诉 Go 直接通过 VCS(git/svn/hg)从源码仓库拉取。
### GOSUMDB 校验链
每个 `go.mod` 旁边都会有一个 `go.sum`,它记录了所有直接和间接依赖的 **模块版本 + 哈希**:
```bash
# 当前 GOSUMDB 设置
go env GOSUMDB # sum.golang.org(Google 托管的全球信任根)
# 离线模式(跳过哈希校验,开发阶段方便但不建议用于 CI)
go env -w GONOSUMCHECK=* # 跳过校验
go env -w GONOSUMDB=* # 不向 sumdb 请求
go env -w GOSUM=off # 完全关闭 sum 检查
```
> [!warning] GONOSUMCHECK vs GONOSUMDB vs GOSUM=off
> | 变量 | 效果 | 适用场景 |
> |------|------|---------|
> | `GONOSUMCHECK=*` | 跳过对 `go.sum` 比对,但仍查询 GOSUMDB | 不推荐使用 |
> | `GONOSUMDB=*` | 不向 sum.golang.org 查询,但仍做 local checksum | 自建镜像时使用 |
> | `GOSUM=off` | 完全禁用 hash 校验 | 仅限纯本地实验项目 |
生产环境务必保留 `GOSUMDB=sum.golang.org`,否则无法防御依赖供应链攻击(比如有人往公共 repo 推送恶意版本)。
### Go Workspace(Go 1.18+)
当你在本地同时修改多个内部模块时,Go Workspace 可以绕过 GOPROXY,用本地路径解析替代网络下载:
```bash
# 初始化 workspace
go work init ./cmd/server ./internal/lib
# 添加已有模块
go work use ./pkg/utils
# 生成的 go.work 文件
cat go.work
# go 1.22
#
# use (
# ./cmd/server
# ./internal/lib
# ./pkg/utils
# )
```
Workspace 模式下,`go build` / `go test` 会优先使用 `use` 列表中本地路径对应的模块版本,而非 `GOPROXY` 下载的缓存版本。这在做 monorepo 或多模块协同开发时非常有用。
### vendor 模式:离线构建
```bash
# 将所有依赖下载到本地 vendor/ 目录
go mod vendor
# 用 vendor 目录构建(完全离线)
go build -mod=vendor ./...
```
`vendor/` 会被纳入代码仓库并提交,这样新成员 clone 后即使没有网络也能编译。对比:
| 方式 | 是否需要网络 | 是否提交 | 适用场景 |
|------|-------------|---------|---------|
| **Module cache** | 首次需要 | 否(本地磁盘) | 日常开发 |
| **vendor/** | 不需要 | 是 | CI/CD、内网隔离 |
| **Go Workspace** | 不需要 | 是(go.work) | 多模块本地协同 |
## 代码示例
### 批量检查过期依赖
```bash
# 使用 depaware 第三方工具可视化依赖关系并发现重复模块
go install github.com/tailscale/depaware@latest
depaware --print ./...
```
### 清理未使用的依赖
```bash
# 移除 go.mod 中不再需要的 require 条目
go mod tidy
# 清理本地 module cache 中无人引用的旧版本
go clean -modcache # ⚠️ 谨慎使用:会删掉整个 ~/go/pkg/mod/cache
```
`go mod tidy` 的核心逻辑:扫描 `*.go` 文件的 import 路径 → 去 `go.mod` 中查找对应 require → 未被导入且无 build tag 使用的条目会被自动删除,新增的导入会自动添加。
## 常见陷阱与最佳实践
### 1. GOPROXY 配错导致 "bad response"
当代理地址不可达时,Go 会报 403/404/502。排查步骤:
```bash
# 第一步:手动 curl 测试代理健康度
curl -i https://goproxy.cn/github.com/aws/aws-sdk-go/@v/v1.45.0.zipinfo
# 期望:HTTP 200 + 响应体包含 {"Path":"github.com/aws/aws-sdk-go", ...}
# 失败:检查代理 URL 是否正确、是否有防火墙拦截
```
阿里云的 `https://mirrors.aliyun.com/goproxy/` 在某些地区更稳定,可切换测试。
### 2. go.sum 丢失或不同步
`go.sum` 不应手动编辑。如果不小心修改了导致冲突:
```bash
# 重建 go.sum(基于 go.mod + 当前可用 GOPROXY 重新拉取)
rm go.sum
go mod download
go mod verify # 确认每个依赖的哈希都匹配
```
### 3. 私有仓库需要结合 GOPRIVATE
```bash
# 跳过代理和 sumdb 校验,走 VCS 直接拉取
go env -w GOPRIVATE=git.internal.company.com/*
```
> [!tip] GOPRIVATE 与 GOPROXY 的关系
> `GOPRIVATE` 模块不走 GOPROXY,也触发 GONOSUMCHECK/GONOSUMDB。如果你只想屏蔽特定域名而不全部关闭校验,可以:
> ```bash
> go env -w GOPRIVATE=git.internal.company.com
> # GOPROXY 仍然走 goproxy.cn,只有匹配的域名例外
> ```
### 4. Module 语义化版本陷阱
Go Modules 使用 [Semantic Import Versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning):如果一个模块 v2+ 发生了 breaking change,它的 import path 应带上 `/v2` 后缀:
```
# v1.x → import "example.com/foo"
# v2.x → import "example.com/foo/v2"
```
很多开源库(如 `gin-gonic/gin`)忽略了这一约定,直接用 `v2.x` 但没有在 import path 上加 `/v2`。这时需要在 `go.mod` 里显式声明:
```go
require github.com/example/broken-module v2.3.4 // indirect
// 或用 replace 纠正
replace github.com/example/broken-module v2.3.4 => github.com/example/broken-module v2.3.4
```
## 延伸阅读
- [[mac-dev-env-setup/homebrew-intro]] — 通过 Homebrew 安装 Go
- [[mac-dev-env-setup/git-ssh-config]] — 私有仓库需先配置 SSH Key