因网络问题导致 Go 语言依赖包下载慢或失败的几个解决方案

Go 语言由 Google 公司开发,由于众所周知的原因,托管 Go 语言包的某些域名可能在国内被屏蔽,比如 golang.orggoogle.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)

要解决这类问题,有以下三种方案:

1、VPN

如果你有可以访问国外站点权限的 VPN,设置通过该 VPN 访问 google.golang.org(这种方式最简单)。

2、git clone && go install

如果没有 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

3、go module

对于 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 路径查找依赖的包管理方式。

4、go module & GOPROXY

对于 Go 1.11 及以上版本,在开启 Go Module 支持的情况下,还可以通过代理服务来完成 Go 依赖包的下载,这样处理起来比上述第三种方案更加快捷,我们无需手动替换下载源,所有依赖会自动从代理的镜像地址下载。

下面我们创建一个新的测试目录 helloworld2,并初始化 Go Module:

go mod init helloworld2

然后运行如下命令导出 GOPROXY 环境变量(Windows 下通过设置系统环境变量 GOPROXY 来实现):

export GOPROXY=https://goproxy.cn

注:以上是七牛云提供的代理地址,还可以使用阿里云提供的代理 https://mirrors.aliyun.com/goproxy/

然后在项目根目录下新增 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.mod 也非常干净:

module helloworld2

go 1.12

require google.golang.org/grpc v1.23.0

main.go 中声明的所有依赖都已经下载完成。

综合比较下来,如果你的 Go 语言版本是1.11+,推荐使用代理的方式来下载 Go 依赖包。

上一篇: panic 和 recover

下一篇: 多进程、多线程与协程的引入