首页 Go get/mod tidy 类库太大问题
文章
取消

Go get/mod tidy 类库太大问题

问题描述

在使用 go getgo mod tidy 时,可能会遇到以下错误:

1
2
3
4
5
# 错误 1
downloaded zip file too large

# 错误 2
create zip: module source tree too large

问题原因

错误 1:downloaded zip file too large

错误 1 是因为 go get 或者 go mod tidy 时,超过了下载 zip 文件的大小限制,默认大小为 500 << 20,即 500MB。

  • 描述:该错误是因为下载的 zip 文件大小超过默认限制 500MB (500 << 20)。
  • 源代码逻辑:在 cmd/go/internal/modfetch/codehost/codehost.go 中,MaxZipFile 定义了下载 zip 文件的最大限制:

    1
    
    const MaxZipFile = 500 << 20 // 解压文件最大限制为 500MB
    
  • 触发流程:在下载模块 zip 文件时,函数检查文件总大小:这里的 repo.Zip 会调用 LimitedReader 来控制读取的字节数。如果实际 zip 文件超过 MaxZipFile,就会触发错误信息 downloaded zip file too large

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    
      // src/cmd/go/internal/modget/get.go
      func init() {
          work.AddbuildFlags(CmdGet, work.OmitModFlag)
          CmdGet.Run = runGet // break init loop
      }
    
      func runGet(ctx context.Context, cmd *base.Command, args []string) {
          ...
          r.wrok.Add(func() {
              if _, err := modfetch.DownloadZip(ctx, mActual); err != nil {
                  ...
              }
          })
          ...
      }
    
      // src/cmd/go/internal/modfetch/fetch.go
      func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err error) {
          ...
          if err := downloadZip(ctx, mod, zipfile); err != nil {
              return cached{"", err}
          }
          ...
      }
    
      func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err error) {
          ...
          err := repo.Zip(f, mod.Version)
          ...
      }
    
      // src/cmd/go/internal/modfetch/coderepo.go
      func (r *codeRepo) Zip(dst io.Writer, version string) error {
          ...
          maxSize := int64(codehost.MaxZipFile)
          lr := &io.LimitedReader{R: dl, N: maxSize + 1}
          ...
      }
    
      // src/cmd/go/internal/modfetch/proxy.go
      func (p *proxyRepo) Zip(dst io.Writer, version string) error {
          ...
          lr := &io.LimitedReader{R: body, N: codehost.MaxZipFile + 1}
          ...
      }
    
      // src/cmd/go/internal/modfetch/codehost/codehost.go
      const (
          MaxGoMod   = 16 << 20    // maximum size of go.mod file
          MaxLICENSE = 16 << 20    // maximum size of LICENSE file
          MaxZipFile = 50000 << 20 // maximum size of downloaded zip file
      )
    

错误 2:create zip: module source tree too large

  • 描述:此错误指解压后的模块源代码树大小超过默认的 500MB 限制。
  • 源代码逻辑:解压文件大小限制由 cmd/vendor/golang.org/x/mod/zip/zip.go 中的 MaxZipFile 定义:

    1
    
    const MaxZipFile = 500 << 20 // 解压文件最大限制为 500MB
    
  • 触发流程:在解压模块 zip 文件时,Create 函数检查文件总大小:checkFiles 函数在解压模块时验证文件大小,如果超出 MaxZipFile 限制,将返回 module source tree too large 错误。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    
      // src/cmd/go/internal/modget/get.go
      func init() {
          work.AddbuildFlags(CmdGet, work.OmitModFlag)
          CmdGet.Run = runGet // break init loop
      }
    
      func runGet(ctx context.Context, cmd *base.Command, args []string) {
          ...
          r.wrok.Add(func() {
              if _, err := modfetch.DownloadZip(ctx, mActual); err != nil {
                  ...
              }
          })
          ...
      }
    
      // src/cmd/go/internal/modfetch/fetch.go
      func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err error) {
          ...
          if err := downloadZip(ctx, mod, zipfile); err != nil {
              return cached{"", err}
          }
          ...
      }
    
      func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err error) {
          ...
          err := repo.Zip(f, mod.Version)
          ...
      }
    
      // src/cmd/go/internal/modfetch/coderepo.go
      func (r *codeRepo) Zip(dst io.Writer, version string) error {
          ...
          return modzip.Create(dst, module.Version{Path: r.modPath, Version: version}, files)
      }
    
      // src/cmd/vendor/golang.org/x/mod/zip/zip.go
      const (
          MaxZipFile = 500 << 20
    
          MaxGoMod = 16 << 20
    
          MaxLICENSE = 16 << 20
      )
    
      func Create(w io.Writer, m module.Version, files []File) (err error) {
          ...
          cf, validFiles, validSizes := checkFiles(files)
          ...
      }
    
      func checkFiles(files []File) (cf CheckedFiles, validFiles []File, validSizes []int64) {
          ...
    
          maxSize := int64(MaxZipFile)
          ...
          size := info.Size()
          if size >= 0 && size <= maxSize {
              maxSize -= size
          } else if cf.SizeError == nil {
              cf.SizeError = fmt.Errorf("module source tree too large (max size is %d bytes)", MaxZipFile)
          }
          ...
      }
    
    

解决方案

临时解决方案

通过修改 Go 源码的文件大小限制并重新编译 Go 来解决问题。

  1. 备份并修改源码路径

    1
    
     mv $GOROOT $GOROOT.new
    
  2. 修改限制值
    • 打开 $GOROOT.new/src/cmd/go/internal/modfetch/codehost/codehost.go,将 MaxZipFile 修改为更大的值(例如 50GB):

      1
      
      const MaxZipFile = 50000 << 20 // 将限制增大至 50GB
      
    • 同时在 $GOROOT.new/src/cmd/vendor/golang.org/x/mod/zip/zip.go 中进行相同修改。

  3. 重新编译 Go

    1
    2
    3
    
     cd $GOROOT.new/src/cmd/go
     go build -o go
     export GOROOT=$GOROOT.new
    
  4. 运行 go getgo mod tidy

    1
    2
    
     $GOROOT.new/src/cmd/go/go get xxx
     $GOROOT.new/src/cmd/go/go mod tidy
    

永久解决方案

在原始 GOROOT 源码中修改后重新编译,使更改永久生效。

  1. 修改源码文件
    • $GOROOT/src/cmd/go/internal/modfetch/codehost/codehost.go
    • $GOROOT/src/cmd/vendor/golang.org/x/mod/zip/zip.go
    • MaxZipFile 调整为更大值,例如 50GB。
  2. 重新编译 Go

    1
    2
    
     cd $GOROOT/src
     ./make.bash
    
  3. 使用 go getgo mod tidy 即可正常运行

参考资料

本文由作者按照 CC BY-NC-SA 4.0 进行授权

如何在 Kubernetes 中手动监测 Pod 流量

[译]MapReduce: 简化大型集群上的数据处理