GO基础总结

Go基础语法参考:https://www.cnblogs.com/vonlinee/p/19005628

环境搭建

# CentOS
yum install go
# Windows 直接官网下载二进制安装包即可

配置环境变量

# 指定Go的安装根目录
export GOROOT=/usr/local/go
# Go工作区目录(自定义,存放你的项目/依赖包,推荐默认)
export GOPATH=$HOME/go
# Goproxy镜像,解决下载依赖超时/失败,七牛云官方镜像
export GOPROXY= https://goproxy.cn ,direct
# 将GO可执行文件加入PATH中,使GO指令与我们编写的GO应用可以全局调用
$PATH=$PATH:$GOBIN:$GOROOT/bin

Windows 上修改GOPATH目录

GOPATH 是 go install 的默认目标目录,第三方依赖会下载到 pkg/mod 目录。建议将 GOPATH 以及相关的 GOCACHE 等目录迁移到非系统盘

go env -w GOPATH=D:\go-workspace
go env -w GOCACHE=D:\go-cache  # 迁移编译缓存

通过 go version 查看安装的版本

➜ go version
go version go1.23.0 windows/amd64

环境变量

这三个是搭建 Go 开发环境最基本、必须了解的变量。

GOROOT

Go 语言安装的根目录。它指向 Go 的 SDK 所在位置,其中包含标准库、编译器、链接器等所有自带的工具。默认值:通常为 /usr/local/go(Unix-like)或 C:\Go(Windows)。现代版本的 Go 通常能自动设置,一般不需要手动修改。除非你安装了多个 Go 版本并需要切换。

GOPATH

在 Go Modules 出现之前,这是所有 Go 代码的工作目录。它有三个子目录:

  1. src(源代码)
  2. pkg(编译后的包文件)
  3. bin(编译后的可执行文件)。

自 Go 1.11 引入 Modules 后,GOPATH 的重要性已大大降低。但它仍然存在,主要用于存放通过 go install 安装的可执行工具(在 $GOPATH/bin 下)以及缓存旧版本(Go 1.4及之前)的包。默认值:$HOME/go(Unix-like)或 %USERPROFILE%\go(Windows)。

GOBIN

执行 go install 命令编译生成的可执行文件的存放目录。运行 go install 时,生成的可执行文件会放在此目录下。为了能在终端任何地方直接运行这些工具,强烈建议将 \(GOBIN 添加到你的系统 PATH 环境变量中。默认值:默认为 `\)GOPATH/bin 。你可以单独设置它,例如 export GOBIN=$HOME/.local/bin`。

命令行工具

go env

会列出所有与 Go 相关的环境变量及其当前值。Windows下输出内容类似于下面这样:

set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\lenovo\AppData\Local\go-build
....
set GOENV=C:\Users\lenovo\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64

查看特定的环境变量:go env VARNAME,在 Windows 的 Command Prompt 或 PowerShell 中,用法基本相同。只是变量名的大小写不敏感,但通常保持大写即可。

➜ go env GOPATH			# 输出: /Users/username/go
➜ go env GOROOT			# 输出: /usr/local/go
➜ go env GOPROXY		# 输出:  https://goproxy.cn ,direct
➜ go env GOPATH GOROOT GOOS GOARCH
C:\Users\lenovo\go
D:\Develop\Go
windows
amd64

使用 -json 标志可以以 JSON 格式输出所有环境变量

go env -json
# 获取特定变量的 JSON 值
go env -json GOROOT GOPROXY

常用环境变量的含义如下

变量名 含义 示例值
GOPATH 工作区目录 /Users/username/go
GOROOT Go 语言安装目录 /usr/local/go
GOBIN 可执行文件安装目录 $GOPATH/bin
GOOS 目标操作系统 darwin, linux, windows
GOARCH 目标架构 amd64, arm64, 386
GOPROXY 模块代理 https://goproxy.cn ,direct
GOMODCACHE 模块缓存目录 $GOPATH/pkg/mod
CGO_ENABLED 是否启用 CGO 1 (启用) 或 0 (禁用)

go build

https://pkg.go.dev/go/build

要使用Go编译单个文件,直接在文件上运行 go build <文件名.go> 即可生成可执行文件(默认与源文件名相同),或者使用 go run <文件名.go> 直接编译并运行。若想指定输出文件名,可用 go build -o <输出名> <文件名.go>。

【go build】指令的作用是:在当前目录下编译生成可执行文件。注意,go build指令会调用所有引用包的源码,重新编译,而不是直接使用pkg里的编译后文件,如果在【\(GOROOT】与【\)GOPATH】下没有找到import引入包的项目源码,就会报错。

go run

go run 命令用于编译并直接运行源代码,它会在后台编译程序(生成临时可执行文件)然后立即执行,执行完毕后删除临时文件,非常适合快速测试和调试,而无需手动生成和管理可执行文件。它的基本语法是 go run [文件名.go] [参数...]。

go install

https://stackoverflow.com/questions/24069664/what-does-go-install-do
go install指令的作用是:编译源代码,如果为可执行文件(package "main"且包含main方法),则会编译生成可执行文件到 $GOPATH/bin目录下;源文件中 import 引入的其他包,就会被编译到$GOPATH/pkg/$GOOS_$GOARCH目录下。格式:go install 包名/包的地址

go install主要用于编译和安装可执行文件,核心目标是将代码编译成可执行文件(或库文件),并将编译产物安装到 Go 语言的标准目录中,方便后续直接调用。

  1. 先执行 go build 的编译逻辑,再把编译结果放到指定目录(区别于 go build 仅编译到当前目录)。
  2. 编译后生成可执行文件,安装到 $GOPATH/bin(或 $GOBIN)目录,可直接在终端运行。

go install 针对包而非单个文件,既可操作本地项目,也可操作远程第三方库,举个例子,在空目录下先初始化模块(Go 1.11+ 推荐),再安装:

go mod init hello
# 在$GOPATH/bin目录下生成hello可执行二进制文件
go install hello

go install / go build

go install也可以完成类似go build的功能,区别在于go install会生成二进制文件到 $GOPATH/pkg/$GOOS_$GOARCH目录下,但go install会把可执行文件自动生成到$GOBIN目录下,这是在一开始被配置到【$PATH】中了,这样就可以使项目可执行文件能被方便的全局调用。

用VSCode打开go代码时,vscode提供的go插件会去执行 go install -v golang.org/x/tools/gopls@latest这个命令,安装@latest版本的gopls这个包,gopls 是 go 官方提供的lsp服务,如果@latest这个版本的gopls和你装的go版本不兼容,则会去下载合适的go版本(估计下的这个版本只是gopls自身用的)

PS C:\Users\lenovo\go> go install -v golang.org/x/tools/gopls@latest
go: downloading golang.org/x/tools v0.41.0
go: downloading golang.org/x/tools/gopls v0.21.0
go: golang.org/x/tools/gopls@v0.21.0 requires go >= 1.25; switching to go1.25.5
go: downloading go1.25.5 (windows/amd64)
......


go get

参考:https://golang.org/doc/go-get-install-deprecation
使用 go get获取 Go 语言的包,go get主要下载依赖,go install主要用于编译和安装可执行文件.

go get 可以理解为git clone到 $GOPATH/src 目录后进行 go install

下载并安装包: go get <包的导入路径>,例如 go get github.com/gin-gonic/gin

导入路径就是代码中写的import的内容,例如

import "github.com/gin-gonic/gin"
  1. 仅下载代码 (不安装): go get -d <包的导入路径>
  2. 更新包到最新版本: go get -u <包的导入路径>
  3. 同时下载测试所需的包: go get -t <包的导入路径>
  4. 下载指定版本的包,只需在包路径后添加 @ 符号和版本号,例如 go get <package_path>@<version>,即可下载该特定版本并更新 go.mod 文件,支持标签 (tag) 或提交哈希。

go get下载的包通常存放在 $GOPATH/pkg/mod目录中(Go Modules模式),如果是可执行文件则放在 $GOPATH/bin目录。具体路径可以通过运行 go env GOPATH命令查看,pkg/mod 缓存了所有依赖项,而 bin 存放编译好的二进制工具.

  1. 依赖包(Modules): $GOPATH/pkg/mod,在这个目录下,Go按模块名称和版本号(如 github.com)存放下载的依赖库.
  2. 可执行文件: $GOPATH/bin,使用 go install 或 go get(针对工具)安装的二进制程序会放在这里,并不是所有包都会有二进制程序

go get github.com/urfave/cli为例,它被下载到了 $GOPATH/pkg/mod 目录下:

$ go get github.com/urfave/cli
go: downloading github.com/urfave/cli v1.22.17
go: downloading github.com/cpuguy83/go-md2man/v2 v2.0.7
go: downloading github.com/russross/blackfriday/v2 v2.1.0
go: added github.com/cpuguy83/go-md2man/v2 v2.0.7
go: added github.com/russross/blackfriday/v2 v2.1.0
go: added github.com/urfave/cli v1.22.17

root@localhost ~/go/pkg/mod/github.com/urfave
drwxr-xr-x 1 root 197121 0  1月 14 22:58 'cli@v1.22.17'/

可以和Github上指定版本的release对比一下:https://github.com/urfave/cli/releases/tag/v1.22.17 ,发现基本上是一样的,估计就和我们直接通过 github 下载是一样的,下载到本地然后解压而已

依赖包的 go 版本和当前项目不一致

每个 Go Module 都可以在自己的 go.mod 中声明 go 版本,例如:

module github.com/example/lib

go 1.25.0

这个版本表示该模块要求的最低 Go 版本。当前项目也有自己的 go.mod

module example.com/app

go 1.23.0

require github.com/example/lib v1.2.0

如果依赖包声明的 go 版本和当前项目不一致,要分情况看。

依赖包 go 版本低于或等于当前项目

例如当前项目是:

go 1.23.0

依赖包是:

go 1.20

这种情况通常没有问题。高版本 Go 可以编译低版本模块,只要依赖包本身没有其他兼容性问题即可。

依赖包 go 版本高于当前项目

例如当前项目是:

go 1.23.0

依赖包是:

go 1.25.0

Go 1.21 之后,主模块的 go 版本必须大于等于所有依赖模块的 go 版本。也就是说,当前项目 go 1.23.0 不能依赖一个要求 go 1.25.0 的模块。

执行 go getgo mod tidygo build 时,可能会看到类似提示:

go: module github.com/example/lib@v1.2.0 requires go >= 1.25.0; switching to go1.25.5
go: upgraded go 1.23.0 => 1.25.0

如果 GOTOOLCHAIN=auto,Go 会尝试自动切换或下载满足要求的 toolchain,并可能自动把当前项目 go.mod 中的 go 行升级到依赖要求的最低版本。

升级后的 go.mod 可能变成:

module example.com/app

go 1.25.0

toolchain go1.25.5

require github.com/example/lib v1.2.0

如果不允许自动切换,例如设置了:

GOTOOLCHAIN=local

那么本机 Go 版本不满足要求时,就会直接报错,提示当前工具链版本太低。这时有三种处理方式:

  1. 升级本机 Go 版本,或者允许 GOTOOLCHAIN=auto 自动下载工具链。
  2. 升级当前项目的 go 版本,让它满足依赖包要求。
  3. 降级依赖包版本,选择一个 go.modgo 版本不高于当前项目的旧版本。

当前项目 go 版本是否一定要跟依赖完全一致

不需要完全一致,只需要满足“当前项目的 go 版本大于等于依赖要求的最低版本”即可。

例如:

// 当前项目
go 1.25.0

// 依赖 A
go 1.20

// 依赖 B
go 1.23

这是正常的。真正有问题的是反过来:

// 当前项目
go 1.23.0

// 依赖 B
go 1.25.0

这种情况下,依赖 B 可能使用了 Go 1.25 才支持的语法、标准库 API 或编译器行为,低版本工具链无法保证正确编译。

实际排查命令

查看当前项目声明的 Go 版本:

go list -m -f '{{.GoVersion}}'

查看某个依赖模块声明的 Go 版本:

go list -m -f '{{.Path}} {{.Version}} go{{.GoVersion}}' github.com/example/lib

查看所有依赖模块的 Go 版本:

go list -m -f '{{.Path}} {{.Version}} go{{.GoVersion}}' all

如果执行这些命令时触发了 toolchain 下载或 go.mod 变更,本质上就是 Go 在维护“主模块 Go 版本不能低于依赖模块 Go 版本”这个约束。

go clean

清理编译生成的临时文件、可执行文件(如 go build 生成的文件)

go doc

查看标准库 / 第三方包的文档(无需联网)
查看 Println 用法go doc fmt Println

[root@localhost bin]# go doc fmt Println
package fmt // import "fmt"

func Println(a ...interface{}) (n int, err error)
    Println formats using the default formats for its operands and writes to
    standard output. Spaces are always added between operands and a newline is
    appended. It returns the number of bytes written and any write error
    encountered.

包管理

最早的时候,Go所依赖的所有的第三方库都放在GOPATH这个目录下面
从v1.5开始开始引入vendor模式,如果项目目录下有vendor目录,那么go工具链会优先使用vendor内的包进行编译、测试等
从v1.11开始,引入了Go Module 作为依赖解决方案,到v1.14宣布Go Module已经可以用于生产环境,到v1.16版本开始Go Module默认开启

GOPATH 模式

GOPATH 模式是 Go 语言在 1.11 版本之前唯一的工作模式。该模式要求所有的 Go 代码(包括自己的项目和第三方依赖库)都必须放在一个单一的工作区目录下,这个目录就是 GOPATH

它没有版本的概念,直接使用代码库的最新代码(默认是 master 分支或默认分支)。

约定有工作目录结构(需要自行创建), 在使用GOPATH的模式下,我们需要将应用代码存放在固定的\(GOPATH/src目录下,并且如果执行`go get`来拉取外部依赖会自动下载并安装到\)GOPATH目录下

  1. src ——存放源代码文件
  2. pkg——存放编译后的文件
  3. bin ——存放编译后的可执行文件*

多个项目最好都在一个 $GOPATH下,即 $GOPATH/src/项目1$GOPATH/src/项目2

# Windows Terminal
cd "$(go env GOPATH)"
  1. bin:用来存放编译后的可执行文件
  2. pkg:用于存放编译后生成的归档文件
  3. src:用来存放go源码文件

第三方套件只要不是官方库,都需要放置在GOPATH/src的路径下才可以使用

当我们想用别人公开在GitHub上的套件,go get 可以帮我们从网络clone到GOPATH/src里面。虽然这样很方便,但是会发现GOPATH/src下会很复杂,除了有你自己开发的代码目录,同时也包含其他第三方库的专属目录

我们给不同的项目设置不同的GoPath,优点非常明显:

便于管理项目,每个项目都是不同的GoPath,这对于我们管理多个Golang项目而言,能够非常清晰的处理项目结构。如果我们把所有项目都放在同一个GoPath的src包下,那么项目的结构就会变得非常混乱,难以管理

但是当我们需要依赖第三方的包的时候,不同的项目设置不同的GoPath的缺点也非常明显:
第三方依赖的包和我们自己的Golang包混在一起,会给我们的项目文件管理带来一定的麻烦
不同的GoPath都需要下载依赖,那么磁盘中重复的依赖就会非常多,会占用我们大量的磁盘空间

GO MODULE

https://golang.org/ref/mod
在1.11开始推出了Go Modules的功能

环境变量 GO111MODULE

这是go使用到的一个环境变量,总共可以设置三种不同的值

  1. auto:默认值,go命令会根据当前目录来决定是否启用modules功能。需要满足两种情形:
    该目录不在GOPATH/src/下,当前或上一层目录存在go.mod文件
  2. on:go命令会使用modules,而不会去GOPATH目录下查找。
  3. off:go命令将不会支持module功能,寻找依赖按照以前GOPATH的做法去寻找

目前1.16版本默认将这个参数设置成on,而且可能之后的版本会弃用掉GO111MODULE,因此建议要开发Go项目时就不再使用GOPATH了,而是采用Go Modules的做法,因此建议都设定为on

采用Go Modules,下载下来的第三方依赖就位于GOPATH/pkg/mod目录下

GO toolchain

Golang工具链:https://go.dev/doc/toolchain

toolchain 可以理解为 Go 编译运行程序时需要用到的一整套工具,包括标准库、编译器、汇编器、链接器以及 go 命令调用的其他工具。Go 1.21 开始,go 命令不仅能使用自己安装包里自带的 toolchain,还可以根据项目要求自动切换到其他 Go 版本,必要时还会自动下载对应版本。

例如本机安装的是 Go 1.23,但当前项目要求 Go 1.25,执行 go buildgo testgo install 等命令时,Go 可能会输出类似下面的提示:

go: module example.com/app requires go >= 1.25; switching to go1.25.5
go: downloading go1.25.5 (windows/amd64)

这不是在全局替换你本机安装的 Go,而是当前这次命令选择了一个满足项目要求的工具链来执行。

go.mod 中的 go 和 toolchain

Go 项目可以在 go.mod 中声明两个和 toolchain 有关的字段:

module example.com/app

go 1.23.0

toolchain go1.25.5

它们的含义不一样:

  1. go 1.23.0:项目最低要求的 Go 版本。低于这个版本的 Go 工具链不应该继续构建这个项目。
  2. toolchain go1.25.5:开发这个项目时推荐使用的 Go 工具链版本。如果当前 go 命令自带的版本低于它,就会尝试切换到这个版本。

如果 go.mod 中没有显式写 toolchain,可以理解为默认使用 go 行对应的工具链。例如:

go 1.23.0

大致等价于:

go 1.23.0
toolchain go1.23.0

所以:

  1. go 更偏向“最低兼容版本”。
  2. toolchain 更偏向“当前开发推荐版本”。
  3. toolchain 不能低于 go 指定的版本。

工具链选择规则

执行 go 命令时,工具链选择大致按下面的顺序:

  1. 先看 GOTOOLCHAIN 环境变量。
  2. 再看当前工作区的 go.work,没有工作区就看当前模块的 go.mod
  3. 如果当前 go 命令自带的版本已经满足 go / toolchain 要求,就直接用本地版本。
  4. 如果本地版本不满足要求,并且允许自动切换,就先在 PATH 中查找类似 go1.25.5 的可执行文件。
  5. 如果 PATH 中找不到,并且允许自动下载,就下载并缓存对应的 toolchain。

可以通过下面命令查看当前选择的工具链版本:

go version

查看 GOTOOLCHAIN 配置:

go env GOTOOLCHAIN

GOTOOLCHAIN

GOTOOLCHAIN 用来控制 go 命令如何选择工具链。Go 官方标准发行版默认是:

GOTOOLCHAIN=auto

常见取值如下:

含义
auto 默认行为。先用本机 Go,如果项目要求更高版本,就自动切换或下载。
local 只使用当前安装的 Go,不自动切换。如果项目要求更高版本,会直接报错。
path 只在 PATH 中查找更高版本的 go1.x.x,不自动下载。
go1.25.5 强制使用指定版本的工具链。
go1.23.0+auto 默认先按 Go 1.23.0 处理,但项目要求更高时仍允许自动切换。

临时指定:

GOTOOLCHAIN=local go test ./...
GOTOOLCHAIN=go1.25.5 go version

持久写入 Go 环境配置:

go env -w GOTOOLCHAIN=auto
go env -w GOTOOLCHAIN=local

如果写错了,想恢复默认配置,可以执行:

go env -u GOTOOLCHAIN

管理 go.mod 中的工具链版本

Go 1.21 之后,gotoolchain 也可以像普通依赖一样通过 go get 管理。

升级项目最低 Go 版本:

go get go@1.25.0

指定推荐工具链:

go get toolchain@go1.25.5

同时指定最低版本和推荐工具链:

go get go@1.23.0 toolchain@go1.25.5

删除 toolchain 行:

go get toolchain@none

需要注意的是,自动下载 toolchain 时,它本质上也是通过 Go 模块机制下载的特殊模块,会受到 GOPROXY 影响;但它不会写入 go.sum。如果把 GOSUMDB 设为 off,工具链下载可能会因为无法校验而失败。

go mod

Go 项目 必须开启 go mod (Go 1.16 及以上版本 默认开启 ,无需手动配置),如果是旧版本 Go,提前执行开启命令:

export GO111MODULE=on  # Linux/Mac 临时开启,永久配置加进.zshrc/.bash_profile
set GO111MODULE=on     # Windows cmd/PowerShell

下面来说明通过命令行创建一个项目的步骤

# 初始化 Go 模块(生成 go.mod 文件,标识项目依赖)
# go mod init <module name> <module name>可填可不填,不填的话预设就是默认的文件名称go.mod
go mod init hello
# 自动整理依赖(添加缺失的依赖、删除无用的依赖,更新 go.mod/go.sum)
go mod tidy
# 下载 go.mod 中声明的依赖到本地缓存
go mod download
# 将依赖复制到项目的 vendor 目录(离线开发用),打包依赖到本地
go mod vendor

在当前目录下创建一个 go.mod 文件

> go mod init hello
go: creating new go.mod: module hello
go: to add module requirements and sums:
        go mod tidy

$ ll
total 1
-rw-r--r-- 1 lenovo 197121 24  1月 14 22:43 go.mod

创建源码文件 main.go

package main

import "fmt"

func main() {
   fmt.Println("Hello, World!")
}

编译并运行:go run main.go

go.mod 文件

项目的「依赖清单」,纯文本文件,在项目根目录,由 go mod init 项目名初始化生成;

在此文件中可以写以下几个关键字:

  1. module:项目模块名
  2. go:定义go语言版本
  3. require:指定项目需要的所有依赖包+对应的版本号,预设是最新版,可以指定版本号
  4. exclude:排除该依赖和其版本
  5. replace:使用不同的依赖版本并替换原有的依赖版本注解,indirect代表被间接导入的依赖包
$ cat go.mod
module hello

go 1.23.0

require (
	// indirect表示传递依赖
        github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
        github.com/russross/blackfriday/v2 v2.1.0 // indirect
        github.com/urfave/cli v1.22.17 // indirect
)

go.sum 文件

只要有开启go modules功能,go get 就不会像以前一样在GOPATH/src下放置依赖,而是会放在GOPATH/pkg/mod里面,并且go.mod会写好引入
项目的「依赖校验文件」,纯文本文件,自动生成,无需手动修改;里面记录了: 所有依赖包的版本哈希值(校验和)

作用:防篡改、验真伪 → 下次下载依赖时,Go 会对比下载包的哈希值和 go.sum记录的是否一致,一致才会使用,避免依赖包被恶意修改或下载到损坏的包;

go mod tidy会同步增删这个文件的校验记录,和 go.mod的依赖清单一一对应。

特别说明:

  1. 如果源文件里只import了内置包,没有第三方包,执行 go mod tidy 是不会生成 go.sum 文件的
  2. 执行 go get 时也会更新 go.sum 文件

go.sum 使用场景

go.sum 不是锁版本文件,它不决定最终使用哪个依赖版本,真正决定依赖版本的是 go.mod 和最小版本选择规则(MVS)。go.sum 的职责是记录“某个模块版本对应的内容哈希”,保证同一个模块版本在任何机器上下载到的内容都是一致的。

典型使用场景有下面几类。

  1. 团队协作和 CI 构建

    代码提交到 Git 后,go.modgo.sum 应该一起提交。其他同事或 CI 服务器执行 go testgo buildgo mod download 时,Go 会根据 go.sum 校验下载到的依赖包内容。如果哈希一致,就说明下载到的是同一份依赖。

  2. 依赖包被篡改

    假设项目里已经记录了某个依赖的校验值:

    github.com/example/lib v1.2.0 h1:abc123...
    github.com/example/lib v1.2.0/go.mod h1:def456...
    

    后来同一个版本 v1.2.0 的源码被人改了,例如作者强推 tag、仓库被入侵、代理缓存被污染,或者公司内部镜像里同版本内容被替换。此时再次下载这个版本,Go 计算出来的哈希和 go.sum 记录的不一致,就会报错并拒绝使用这个依赖。

    类似错误如下:

    verifying github.com/example/lib@v1.2.0: checksum mismatch
        downloaded: h1:xxxxxx...
        go.sum:     h1:abc123...
    
    SECURITY ERROR
    This download does NOT match an earlier download recorded in go.sum.
    

    这就是 go.sum 防止“依赖投毒”的核心价值:依赖名字和版本号没变,但内容变了,Go 可以识别出来。

  3. 防止代理或网络返回错误内容

    Go 下载依赖时可能经过 GOPROXY,例如:

    go env GOPROXY
    #  https://goproxy.cn ,direct
    

    如果代理服务返回了错误的 zip 包,或者网络中间层返回了损坏内容,go.sum 校验会失败。这样可以避免“下载成功但实际内容不对”的问题。

  4. 校验本地模块缓存

    依赖下载后会缓存在 $GOPATH/pkg/mod。如果本地模块缓存被手动改过、磁盘损坏、杀毒软件误处理,也可能导致依赖内容和 go.sum 不一致。可以使用下面命令检查本地缓存是否和 go.sum 匹配:

    go mod verify
    

    正常输出:

    all modules verified
    

    如果发现模块被修改,会提示对应模块校验失败。处理方式通常是清理模块缓存后重新下载:

    go clean -modcache
    go mod download
    
  5. 新机器首次下载依赖

    如果本地没有某个依赖,Go 会先下载依赖,再校验它的哈希。如果 go.sum 中已经有记录,就直接和本地记录比对;如果没有记录,Go 会查询 checksum database(默认是 sum.golang.org)来确认这个模块版本的公开校验值,然后把结果写入 go.sum

go.sum 和 checksum database

go.sum 是项目本地保存的校验记录,checksum database 是 Go 官方提供的公共校验数据库,默认地址是:

sum.golang.org

两者配合使用:

  1. go.sum 已有记录:优先使用本地记录校验。
  2. go.sum 没有记录:Go 会去 checksum database 查询这个模块版本的可信哈希。
  3. 下载内容、go.sum、checksum database 三者不一致时,Go 会拒绝使用该依赖。

如果是公司私有仓库,checksum database 查不到私有模块,需要配置 GOPRIVATEGONOSUMDB 跳过公共校验库:

go env -w GOPRIVATE=gitlab.company.com/*
go env -w GONOSUMDB=gitlab.company.com/*

不建议随便设置:

GOSUMDB=off

因为这样会关闭公共模块的 checksum database 校验,依赖投毒风险会更高。只有在完全内网、无法访问公共校验库,并且有可信私有代理或私有校验机制时,才考虑关闭。

go.sum 里为什么有 /go.mod 记录

go.sum 中经常能看到两类记录:

github.com/example/lib v1.2.0 h1:xxxxxx...
github.com/example/lib v1.2.0/go.mod h1:yyyyyy...

第一行校验的是整个模块源码压缩包,第二行校验的是这个模块版本的 go.mod 文件。Go 有时候只需要读取依赖的 go.mod 来做版本选择,不一定需要下载整个源码包,所以会单独记录 /go.mod 的哈希。

简单记:

  1. module version h1:...:校验依赖源码内容。
  2. module version/go.mod h1:...:校验依赖的模块声明文件。
  3. 两者都很重要,不能手动乱删。

go mod tidy

自动同步项目的依赖关系,让 go.modgo.sum 文件,与项目中 import 导入的包 保持绝对一致 。这包含两层意思:

1. 清理冗余依赖

2. 补齐缺失依赖

这个命令的执行逻辑就是「双向校验 + 自动修正」,执行时会 全盘扫描你的项目所有.go 源码文件 ,分析所有 import语句,然后执行:

自动下载【缺失】的依赖包

你的代码里 import了某个第三方包 / 自定义包,但 go.mod/go.sum 里没有记录这个依赖、本地也没有缓存 → go mod tidy 会自动从远程仓库(通过你的 GOPROXY配置)下载这个依赖,同时把依赖的 版本信息校验信息分别写入 go.modgo.sum 文件,保证项目能正常编译运行。

你从 Git 拉取了一个别人的 Go 项目,执行 go run main.go提示 missing module xxx,执行 go mod tidy即可一键补齐所有缺失依赖。

自动删除【未使用】的冗余依赖

你的 go.mod/go.sum 里记录了某些依赖包,但你的项目源码中没有任何地方 import / 使用这些包 → go mod tidy 会自动把这些「无用依赖」从 go.mod(require 段)和 go.sum 中删除,同时清理本地缓存中对应的无用包。

场景举例:你开发时引入了 github.com/gin-gonic/gin,后来需求变更删掉了相关代码,此时 go.mod还残留这个依赖,执行命令即可一键清理。

依赖包的下载位置

go mod tidy下载的所有依赖包, 不会下载到你的项目目录里 ,而是统一缓存到 系统全局缓存目录

  • Linux/Mac 系统:$GOPATH/pkg/mod (默认 GOPATH~/go,完整路径 ~/go/pkg/mod
  • Windows 系统:%GOPATH%\pkg\mod

优点:所有 Go 项目共享一份依赖缓存,不会重复下载,节省磁盘空间;同一个依赖包同一个版本,本地只会存一份。

依赖包的版本选择

go mod tidy会遵循「 最小可用版本 」原则自动选择依赖版本:

  1. 优先匹配 go.mod中已声明的版本;
  2. 如果未声明,会自动下载该依赖的 最新稳定版
  3. 如果项目有版本兼容问题,你可以手动在 go.mod中指定版本,再执行 go mod tidy即可生效。

其他相关环境变量

  1. GOMODCACHE
    • 含义:Go Module 的缓存目录。所有下载的依赖包(包括特定版本)都会存储在这里。
    • 默认值$GOPATH/pkg/mod
    • 用途:你可以通过设置此变量来改变依赖包的缓存位置,例如将其放在更大的硬盘分区上。
  2. GOPROXY
    • 含义:Go Module 代理服务器的地址。用于加速依赖包的下载和提供稳定的可用性。
    • :是一个逗号分隔的 URL 列表,也可以是特殊值 direct(直连代码仓库)或 off(禁用网络)。
    • 推荐设置 https://goproxy.cn ,direct(适用于中国大陆,将 goproxy.io 或官方 proxy.golang.org 替换为国内镜像源以大幅提升下载速度)。
    • 工作流程:Go 工具会按顺序尝试列表中的代理,直到成功为止。最后的 direct 表示如果所有代理都失败,则直接尝试连接版本控制系统(如 GitHub)。
  3. GOSUMDB
    • 含义:Go Checksum Database 的地址。用于验证下载的依赖包是否未被篡改,确保安全。
    • 默认值sum.golang.org
    • 注意:访问此数据库可能较慢或不可用。如果你使用了可信的国内代理(如 goproxy.cn),通常会配备自己的校验数据库,通常不需要单独修改。在特殊内网环境下可设置为 off 来关闭校验(不推荐)。
  4. GOPRIVATE / GONOPROXY / GONOSUMDB
    • 用于控制哪些私有模块(例如公司内部的 GitLab 仓库)通过公共代理 (GOPROXY) 下载,也进行公共校验和校验 (GOSUMDB)。
    • :逗号分隔的模块路径前缀 glob 模式列表(如 *.company.com,github.com/yourname/private-repo)。
    • GOPRIVATE 设置了,就自动同时设置了 GONOPROXYGONOSUMDB
    • 也可以单独精细控制 GONOPROXYGONOSUMDB

下载包

默认情况下,go 命令可能会从 https://proxy.golang.org 下载模块。
它可以使用位于https://sum.golang.org的校验和数据库来验证模块。这两项服务均由谷歌的Go团队运营。

go 命令的下载行为可以通过 GOPROXY、GOSUMDB、GOPRIVATE 和其他环境变量进行配置。有关更多信息,请参阅“go help environment”和 https://golang.org/ref/mod#private-module-privacy

go vendor

go vendor 命令(通常指 go mod vendor 或第三方工具 govendor)是 Go 语言中管理项目依赖的机制,其核心是将项目所需的所有依赖包复制到项目根目录下的 vendor 文件夹中,以实现离线构建、版本锁定和保证构建一致性,其中 go mod vendor 是 Go 1.14+ 推荐的官方方式。

  1. go mod vendor: 创建或更新 vendor 目录,将 go.mod 中声明的依赖及其版本复制到 vendor。
  2. go build -mod=vendor: 使用 vendor 目录中的依赖进行构建。
    作用: 确保项目在任何环境都能使用指定版本的依赖,实现可重复构建。

这种模式下:包 github.com/$USER/$REPO会在本地目录 vendor/github.com/$USER/$REPO/目录下进行搜索

> go mod vendor
go: go.mod file not found in current directory or any parent directory; see 'go help modules'

注意:如果 vendor 的依赖列表和 go.mod 文件中的依赖列表不一致时

go: inconsistent vendoring in \the\path\to\project:
	github.com/fatih/color@v1.18.0: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
	github.com/mattn/go-colorable@v0.1.13: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
	github.com/mattn/go-isatty@v0.0.20: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
	golang.org/x/sys@v0.25.0: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt

	To ignore the vendor directory, use -mod=readonly or -mod=mod.
	To sync the vendor directory, run:
		go mod vendor

离线安装依赖

https://stackoverflow.com/questions/69603001/golang-offline-install-third-party-package

首先需要一台有网络的机器,以下面这个 go.mod 文件为例:

module hello
go 1.23.0
require github.com/urfave/cli/v3 v3.6.2 // indirect

执行 go mod vendor,将所有依赖项下载到项目目录下的 vendor/ 目录中。如下所示:/github.com/urfave/cli/v3 目录中包含了这个包的所有源码

└─vendor
    └─github.com
        └─urfave
            └─cli
                └─v3
                ......
	└─module.txt

然后将 vendor 目录复制到没有网络的机器

如果内网环境,只有一台机器,只有包的 github 地址,如何进行包的安装?

GOPROXY 代理配置

GOPROXY 是一个以逗号分隔的 URL / 关键字列表,Go 工具链(go get/go mod)会按顺序尝试从列表中的地址拉取模块依赖。核心作用:

  1. 加速依赖下载(尤其国内访问海外仓库)。
  2. 解决网络限制导致的依赖拉取失败。
  3. 提供稳定、可缓存的模块镜像服务。用于内网或者公有镜像

设置 CDN 加速代理,以下是几个速度不错的提供者:

  1. 七牛:Goproxy 中国 https://goproxy.cn
  2. 阿里: mirrors.aliyun.com/goproxy/
  3. 官方: < 全球 CDN 加速 https://goproxy.io/>
  4. 其他:jfrog 维护 https://gocenter.io

在 Linux 或 macOS 上面,需要运行下面命令(或者,可以把以下命令写到 .bashrc 或 .bash_profile 文件中):

# 启用 Go Modules 功能
go env -w GO111MODULE=on

# 配置 GOPROXY 环境变量,以下三选一
# direct:直连模式,绕过代理,直接从 Git 等源码仓库拉取。
# 1. 七牛 CDN
go env -w  GOPROXY= https://goproxy.cn ,direct
# 2. 阿里云
go env -w GOPROXY= https://mirrors.aliyun.com/goproxy/ ,direct
# 3. 官方
go env -w  GOPROXY= https://goproxy.io ,direct

Windows 上配置:

# 启用 Go Modules 功能
$env:GO111MODULE="on"
# 配置 GOPROXY 环境变量,以下三选一
# 1. 七牛 CDN
$env:GOPROXY=" https://goproxy.cn ,direct"
# 2. 阿里云
$env:GOPROXY=" https://mirrors.aliyun.com/goproxy/ ,direct"
# 3. 官方
$env:GOPROXY=" https://goproxy.io ,direct"

通过 go env GOPROXY 验证是否设置成功

GONOSUMDB

GOPROXY 解决包的下载问题,如果是内网环境,即使通过GOPROXY配置了镜像,执行go get 命令仍然失败,类似下面的错误:

go: github.com/frankban/quicktest@v1.14.2: verifying module: github.com/frankban/quicktest@v1.14.2: Get "https://sum.golang.org/lookup/github.com/frankban/quicktest@v1.14.2": Connection Timeout

Go 为了保证模块下载的安全性,会从 GOSUMDB(默认是 sum.golang.org)获取每个模块的校验和(哈希值),并和本地下载的模块做比对。

set GOSUMDB=sum.golang.org

GONOSUMDB 用于指定不需要校验和验证的模块匹配规则;
核心作用:对私有仓库、内网模块或无法访问 GOSUMDB 的模块,跳过校验步骤,避免 go mod 操作时报错。

GONOSUMDB 的值是逗号分隔的模块路径匹配模式,支持两种写法:

  1. 精确匹配:如 github.com/your-company/private-repo;
  2. 通配符匹配:用 * 匹配任意前缀 / 后缀,如 .your-company.com(匹配所有内网域名下的模块)、github.com/your-company/(匹配公司名下所有仓库)。

跳过所有内网模块和公司私有仓库的校验

export GONOSUMDB="*.internal.com,github.com/your-team/*"

go env -W GOSUMDB=sum.golang.org

使用场景

  1. 私有模块:你的公司私有 Git 仓库(如 gitlab.your-company.com)的模块,不会被提交到公共的 GOSUMDB,如果不配置 GONOSUMDB,go mod 会因找不到校验和而报错;
  2. 内网模块:内网部署的模块仓库,无法被 sum.golang.org 抓取,自然没有校验和;
  3. 特殊代理场景:部分代理服务器未同步校验和,导致校验失败时,可临时跳过。

GONOSUMDB

不需要校验的包的白名单配置

go env -W GONOSUMDB=sum.golang.org

GOPRIVATE 私有模块

如果使用的 Go 版本 >=1.13, 可以通过设置 GOPRIVATE 环境变量来控制哪些私有仓库和依赖 (公司内部仓库) 不通过 proxy 来拉取,直接走本地:

# Go version >= 1.13
go env -w GOPROXY= https://goproxy.cn ,direct
# 设置不走 proxy 的私有仓库,多个用逗号相隔
go env -w GOPRIVATE=*.corp.example.com

参考资料

  1. https://www.cnblogs.com/ssgeek/p/14843476.html
posted @ 2025-09-18 22:04  vonlinee  阅读(90)  评论(0)    收藏  举报