首页 › Go 模块下载超时
go mod download 超时:模块代理通了,校验库可能还没通
配好了国内的模块代理,go get 依旧卡住,报错里带着 dial tcp 和 i/o timeout。反复换代理地址、反复清缓存都没有变化——这类情况里最常见的原因是,Go 拉取一个模块需要走的不止一条路,而你只处理了其中一条。
两条路,作用不同
Go 在获取一个模块时,默认会依次用到两套服务:模块代理负责把模块的源码打包好交给你;校验库负责告诉你这份代码的哈希是否和全世界第一次见到它时一致。
据 Go 官方文档对 GOPROXY 与 GOSUMDB 的说明,模块代理与校验和数据库是两个独立配置项,各自有自己的默认地址,也各自可以被单独覆盖或关闭。这句话是整个问题的核心:它们是两条独立的路,配好一条不代表另一条也通。
常见的失败形态就是这样产生的:你把 GOPROXY 指向了一个国内可达的地址,模块本身能顺利下载;但 GOSUMDB 仍然指着默认的境外地址,于是每个新模块在校验环节卡住,超时之后整条命令失败。报错看上去像是下载失败,实际失败在校验。
怎么确认是哪一条卡住
最快的办法是看报错里出现的主机名。如果超时的地址是你配置的那个代理地址,问题在代理这条路;如果超时的地址是校验库的默认地址,或者报错里出现 verifying module 这类措辞,问题在校验这条路。
先把当前的实际配置打印出来看一眼,不要凭记忆判断:go env GOPROXY GOSUMDB GONOSUMDB GOPRIVATE GOFLAGS。这几个值可能来自环境变量、来自 Go 自己的配置文件、也可能被项目里的设置覆盖,实际生效的那个未必是你以为的那个。这一步能排掉相当一部分「我明明配了」的情况。
另一个有用的信号是第二次执行是否变快。如果第一次超时、第二次瞬间成功,说明第一次已经把内容拿到并缓存了,卡的是某个一次性步骤;如果每次都在相同位置超时,说明那条路始终没通。
缓存和私有模块这两个变量
Go 会把下载过的模块缓存到本地。这带来一个容易误判的现象:同一个项目在你的机器上能构建、在同事的机器上或者在持续集成里失败。原因不是配置不同,而是你的缓存里已经有了,而对方没有,于是只有对方需要真正走一次网络。
判断方法:把缓存清掉之后再试一次,如果这时你的机器也失败了,那问题一直存在,只是被缓存掩盖着。这一步在排查前值得先做,否则你可能在一台已经不需要联网的机器上反复验证配置。
私有模块是另一个变量。指向公司内部或者自建仓库的模块不应该走公共代理,也不应该送去公共校验库比对哈希——那既没有意义,也可能把内部路径泄露出去。Go 为此提供了单独的配置项来划出私有范围,如果这一部分没有正确划分,私有模块会被送上公共链路,然后在那里超时。
报错文本怎么读
dial tcp ... i/o timeout:连接建立不起来。这是路径层面的问题——目标地址不可达,或者中途被丢弃。看清超时的是哪个主机名,就知道是哪条路。
unrecognized import path:解析阶段就失败了,通常是模块路径本身写错,或者需要走的仓库不可达导致无法探测。这一类要先确认路径拼写。
checksum mismatch:这条和超时性质完全不同,不要当成网络问题处理。它意味着拿到的内容哈希和校验库记录的不一致,可能是上游改动了已发布的版本,也可能是中途内容被替换。遇到它应该停下来查清原因,而不是想办法绕过校验。
410 Gone 或者代理返回的其他状态码:说明代理这条路是通的、也回答了,只是它那边没有这个模块或版本。这属于代理内容问题,不是连通性问题。
排查顺序
第一步,清缓存再试。确认问题在当前机器上真实存在,而不是被本地缓存掩盖。
第二步,打印实际配置。用 go env 看清生效的值,而不是看你写在某个文件里的值。
第三步,读超时的主机名。确定卡的是模块代理还是校验库,两者的处理方式不同。
第四步,划清私有范围。确认内部模块没有被送上公共链路。
第五步,如果两条路都需要访问境外地址且都不通,那是路径问题。这一层用国内镜像可以解决一部分,但校验库这条路的镜像可用性不如模块代理,遇到必须访问默认地址的场景时,让整台机器有一条稳定通道通常比逐个服务找镜像更省事。
命令行工具各有各的代理字段,容易漏;Windows 端整机一条通道,就免掉逐个填这件事。安卓与 Windows 客户端已上线,macOS 与 Linux 开发中。iPhone 暂无原生客户端。注册即可使用永久免费套餐,按月发放流量额度。
据 Go 官方文档对 GOPROXY 与 GOSUMDB 的说明,模块代理与校验和数据库是两个独立配置项,各自有自己的默认地址。
常见问题
已经把 GOPROXY 换成国内地址了,为什么还超时?
因为模块代理和校验库是两条独立的路。GOPROXY 只管模块源码的下载,校验和数据库有自己的配置项和默认地址。代理换成国内之后模块能下载,但每个新模块仍要送去校验库比对哈希,那一步走的是另一个地址,不通就依旧超时。
怎么知道卡住的是代理还是校验库?
看报错里超时的主机名。是你配置的代理地址就在代理这条路,是校验库的默认地址或者报错里出现 verifying module 这类措辞就在校验这条路。另外先用 go env 打印实际生效的配置,不要凭记忆判断。
为什么我本地能构建,持续集成里就失败?
大概率是本地缓存。Go 会缓存下载过的模块,你的机器上已经有了就不需要真正联网,而干净环境每次都要走一次网络。清掉缓存后在本地再试一次,如果也失败,说明问题一直存在,只是被缓存掩盖着。
报 checksum mismatch 是网络问题吗?
不是,而且不应该当网络问题处理。它意味着拿到的内容哈希和校验库记录的不一致,可能是上游改动了已发布版本,也可能是内容在中途被替换。遇到它应该停下来查清原因,不要想办法绕过校验。
公司内部的私有模块也要走这两条路吗?
不应该。私有模块走公共代理没有意义,送去公共校验库比对哈希还可能把内部路径泄露出去。Go 提供了单独的配置项来划出私有范围,如果没有正确划分,私有模块会被送上公共链路然后在那里超时。
第一次超时、第二次就成功,是什么情况?
说明第一次已经把内容拿到并缓存了,卡住的是某个一次性步骤。如果每次都在相同位置超时,那说明那条路始终没通,两种情况的排查方向不同。
代理返回 410 Gone 是路不通吗?
不是。能收到状态码说明代理这条路通了、也回答了,只是它那边没有这个模块或版本。这属于代理的内容问题,不是连通性问题,换代理或者确认版本号更有意义。
校验库也有国内镜像可以用吗?
模块代理的国内镜像选择较多,校验和数据库这条路的镜像可用性明显不如前者。当遇到必须访问默认地址的场景时,让整台机器有一条稳定通道通常比逐个服务寻找镜像更省事。
继续阅读:首页 · 依赖装不上 · Python 装包超时 · 推拉代码断流