Go 语言由 Google 公司开发,由于众所周知的原因,托管 Go 语言包的某些域名可能在国内被屏蔽,比如 golang.org、google.com 等,因此,当我们下载托管在这些域名的包时,会出现网络问题导致的下载失败,以 google.golang.org/grpc 这个包为例,当我们下载它时,会出现如下错误提示:
即:
Fetching https://google.golang.org/grpc?go-get=1
https fetch failed: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
要解决这类问题,有以下三种方案:
友情提示:最新版本 Golang(1.11+)建议使用 Go Moudles + GoProxy 管理依赖。
如果你有可以访问国外站点权限的 VPN,设置通过该 VPN 访问 google.golang.org(这种方式最简单)。
如果没有 VPN,Go 语言版本又在 1.11 以下,不支持 Go Module 功能,可以通过如下方式从 github.com 将对应项目克隆到本地 $GOPATH/src 目录下:
cd $GOPATH/src
git clone https://github.com/grpc/grpc-go.git google.golang.org/grpc
此外,还要克隆 google.golang.org/grpc 依赖的其他包(依赖哪些包可以在运行 go install 报错时看到):
git clone https://github.com/golang/net.git golang.org/x/net
git clone https://github.com/golang/text.git golang.org/x/text
git clone https://github.com/google/go-genproto.git google.golang.org/genproto
接下来,通过如下命令安装 Golang Protobuf 包:
go get -u -v github.com/golang/protobuf/proto
go get -u -v github.com/golang/protobuf/protoc-gen-go
最后,通过 go install 命令手动安装这个扩展包:
go install google.golang.org/grpc
对于 Go 1.11 及以上版本,支持 Go Module 功能,Go Module 是一个新型的包管理工具,我们创建一个新项目,比如 helloworld,并且在该目录下运行如下命令来初始化 Go Module:
go mod init helloworld
这会在当前目录下生成一个 go.mod 文件:
然后在当前目录下新建一个测试文件 main.go 并初始化代码如下:
package main
import "google.golang.org/grpc"
func main() {
grpc.NewServer()
}
接下来运行如下命令将 google.golang.org/grpc 源替换为 github.com/grpc/grpc-go@latest:
go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
然后下载并更新依赖,它会自动下载 main.go 中声明的导入包:
go mod tidy
此时会报错:
说明 google.golang.org/grpc 依赖其他包,我们需要一一将对应源替换为 github.com 项目:
go mod edit -replace=golang.org/x/tools@v0.0.0-20190524140312-2c0ae7006135=github.com/golang/tools@v0.0.0-20190524140312-2c0ae7006135
go mod edit -replace=google.golang.org/genproto@v0.0.0-20180817151627-c66870c02cf8=github.com/google/go-genproto@v0.0.0-20180817151627-c66870c02cf8
go mod edit -replace=google.golang.org/appengine@v1.1.0=github.com/golang/appengine@v1.1.0
go mod edit -replace=golang.org/x/oauth2@v0.0.0-20180821212333-d2e6202438be=github.com/golang/oauth2@v0.0.0-20180821212333-d2e6202438be
go mod edit -replace=golang.org/x/net@v0.0.0-20190311183353-d8887717615a=github.com/golang/net@v0.0.0-20190311183353-d8887717615a
go mod edit -replace=golang.org/x/sys@v0.0.0-20190215142949-d0b11bdaac8a=github.com/golang/sys@v0.0.0-20190215142949-d0b11bdaac8a
go mod edit -replace=golang.org/x/lint@v0.0.0-20190313153728-d0100b6bd8b3=github.com/golang/lint@v0.0.0-20190313153728-d0100b6bd8b3
go mod edit -replace=cloud.google.com/go@v0.26.0=github.com/googleapis/google-cloud-go@v0.26.0
再次运行 go mod tidy 即可,如果运行过程中出现其他 golang.org 域名下的包下载失败,则继续替换。go mod tidy 执行成功后会在当前目录下生成一个 go.sum 文件,用于存放每个依赖库的版本和哈希值。
最后把依赖复制到 vendor 目录:
go mod vendor
这个时候,你可以看到 helloworld 的目录结构变成了这样子:
接下来,我们可以通过从 vendor 目录查找依赖来构建程序,该命令会在当前目录下生成 helloworld 二进制程序:
go build -mod=vendor
这就脱离了传统的通过 $GOPATH 路径查找依赖的包管理方式。
对于 Go 1.11 及以上版本,在开启 Go Module 支持的情况下(1.13及以上版本默认开启),还可以通过代理服务来完成 Go 依赖包的下载,这样处理起来比上述第三种方案更加快捷,我们无需手动替换下载源,所有依赖会自动从代理的镜像地址下载。
下面我们创建一个新的测试目录 helloworld2,并初始化 Go Module:
go mod init helloworld2
然后运行如下命令导出 GOPROXY 环境变量(Windows 下通过设置系统环境变量 GOPROXY 来实现):
export GOPROXY=https://goproxy.cn
注:以上是七牛云提供的代理地址,还可以使用阿里云提供的代理 https://mirrors.aliyun.com/goproxy/,或者 Go 官方提供的全球代理 https://goproxy.io。
如果你是在 GoLand 中开发,还可以通过 Preferences->Go->Go Modules 来配置 Proxy 设置:
或者干脆直接通过 Go Modules 创建新项目:
然后在项目根目录下新增 main.go,并初始化代码如下(和第三种方案一样):
package main
import "google.golang.org/grpc"
func main() {
grpc.NewServer()
}
接下来,运行 go mod tidy 自动下载依赖包 google.golang.org/grpc,除了该依赖包之外,依赖包本身依赖的其他依赖包也会通过镜像代理地址下载,不需要一个个去替换下载源,非常快捷方便。
接下来,可以运行 go mod vendor 将依赖复制到 vendor 目录下,此时我们会看到项目目录结构已经和第三步一样了:
注:go vendor 操作可选,官方不建议这么做。
go.mod 也非常干净:
module helloworld2
go 1.12
require google.golang.org/grpc v1.23.0
main.go 中声明的所有依赖都已经下载完成。
综合比较下来,如果你的 Go 语言版本是1.11+,推荐使用代理的方式来下载 Go 依赖包。