获取所有Golang包列表的最佳实践是什么

获取所有Golang包列表的最佳实践是什么 首先,我要感谢这个热情社区中的每一个人。

我想索引几乎所有当前可用的包,并在一个搜索页面上展示它们(类似于 pkgsite 所做的工作)。

换句话说,我正在开发一个名为 Goggle 的 Go 语言替代 API 搜索引擎,所以我需要定期索引每一个包。

一个实现了[模块代理协议]的模块镜像。对于需要下载大量模块的用户(例如用于批量静态分析),该镜像支持一个单独的端点 /cached-only,该端点指示代理仅返回缓存的内容。

我知道可以通过 proxy.golang.org 下载包而无需克隆每个 git 历史记录,但是,我不确定如何获取要索引的包的完整列表。

index.golang.org 的工作方式类似于“时间线”,这使得仅获取最新版本变得具有挑战性。我必须向后搜索才能做到这一点,但由于只存在一个 ‘since’ 参数,而没有 ‘before’ 参数,所以这并不简单。

那么,您使用了什么方法来快速获取仅包含最新包的列表?是否有自动化的工具或库已经实现了这个功能?或者是否有其他在线代理允许仅检索最新版本,而无需每次都抓取 index.golang.org

欢迎任何建议,非常感谢任何帮助!


更多关于获取所有Golang包列表的最佳实践是什么的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于获取所有Golang包列表的最佳实践是什么的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


要获取所有Go包的最新版本列表,最有效的方法是结合使用index.golang.org的时间线接口和模块代理的/list端点。以下是具体实现方案:

方案一:使用index.golang.org的增量索引

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"
)

type IndexEntry struct {
    Path      string    `json:"Path"`
    Version   string    `json:"Version"`
    Timestamp time.Time `json:"Timestamp"`
}

func fetchAllPackages(since time.Time) ([]IndexEntry, error) {
    var allEntries []IndexEntry
    client := &http.Client{Timeout: 30 * time.Second}
    
    for {
        url := fmt.Sprintf("https://index.golang.org/index?since=%s", 
            since.Format(time.RFC3339))
        
        resp, err := client.Get(url)
        if err != nil {
            return nil, err
        }
        
        decoder := json.NewDecoder(resp.Body)
        for {
            var entry IndexEntry
            if err := decoder.Decode(&entry); err == io.EOF {
                break
            } else if err != nil {
                resp.Body.Close()
                return nil, err
            }
            
            allEntries = append(allEntries, entry)
            since = entry.Timestamp
        }
        resp.Body.Close()
        
        // 如果返回的条目少于1000个,说明已经获取完当前所有数据
        // 实际实现中需要根据响应头或内容判断
        break
    }
    
    return allEntries, nil
}

方案二:使用模块代理的/list端点(推荐)

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strings"
)

type ModuleInfo struct {
    Versions []string          `json:"versions"`
    Latest   string            `json:"latest"`
}

func fetchLatestVersions() (map[string]string, error) {
    modules := make(map[string]string)
    
    // 获取所有已知模块的列表
    resp, err := http.Get("https://proxy.golang.org/list")
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var moduleList []string
    if err := json.NewDecoder(resp.Body).Decode(&moduleList); err != nil {
        return nil, err
    }
    
    // 并发获取每个模块的最新版本
    sem := make(chan struct{}, 10) // 限制并发数
    results := make(chan struct {
        path    string
        version string
        err     error
    }, len(moduleList))
    
    for _, modulePath := range moduleList {
        go func(path string) {
            sem <- struct{}{}
            defer func() { <-sem }()
            
            url := fmt.Sprintf("https://proxy.golang.org/%s/@latest", path)
            resp, err := http.Get(url)
            if err != nil {
                results <- struct {
                    path    string
                    version string
                    err     error
                }{path, "", err}
                return
            }
            defer resp.Body.Close()
            
            var info ModuleInfo
            if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
                results <- struct {
                    path    string
                    version string
                    err     error
                }{path, "", err}
                return
            }
            
            results <- struct {
                path    string
                version string
                err     error
            }{path, info.Latest, nil}
        }(modulePath)
    }
    
    // 收集结果
    for i := 0; i < len(moduleList); i++ {
        result := <-results
        if result.err == nil && result.version != "" {
            modules[result.path] = result.version
        }
    }
    
    return modules, nil
}

方案三:使用goproxy.io的增强API

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

func fetchAllFromGoproxy() ([]string, error) {
    // goproxy.io提供更友好的批量接口
    resp, err := http.Get("https://goproxy.io/stats/summary")
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var stats struct {
        Modules []struct {
            Path string `json:"path"`
        } `json:"modules"`
    }
    
    if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
        return nil, err
    }
    
    var modules []string
    for _, m := range stats.Modules {
        modules = append(modules, m.Path)
    }
    
    return modules, nil
}

批量处理优化示例

package main

import (
    "bufio"
    "context"
    "fmt"
    "os"
    "sync"
    "time"
)

type PackageIndexer struct {
    proxyURL string
    cache    sync.Map
}

func NewPackageIndexer() *PackageIndexer {
    return &PackageIndexer{
        proxyURL: "https://proxy.golang.org",
    }
}

func (p *PackageIndexer) BatchIndex(modulePaths []string) error {
    var wg sync.WaitGroup
    batchSize := 100
    
    for i := 0; i < len(modulePaths); i += batchSize {
        end := i + batchSize
        if end > len(modulePaths) {
            end = len(modulePaths)
        }
        
        batch := modulePaths[i:end]
        wg.Add(1)
        
        go func(batch []string) {
            defer wg.Done()
            p.indexBatch(batch)
        }(batch)
        
        // 避免速率限制
        time.Sleep(100 * time.Millisecond)
    }
    
    wg.Wait()
    return nil
}

func (p *PackageIndexer) indexBatch(modules []string) {
    for _, module := range modules {
        // 获取模块信息
        info, err := p.fetchModuleInfo(module)
        if err != nil {
            continue
        }
        
        // 存储到缓存或数据库
        p.cache.Store(module, info)
    }
}

使用go命令直接获取

package main

import (
    "os/exec"
    "strings"
)

func getGoListAll() ([]string, error) {
    // 注意:这需要本地有完整的模块缓存
    cmd := exec.Command("go", "list", "-m", "all")
    output, err := cmd.Output()
    if err != nil {
        return nil, err
    }
    
    lines := strings.Split(string(output), "\n")
    var modules []string
    for _, line := range lines {
        if line != "" {
            modules = append(modules, line)
        }
    }
    
    return modules, nil
}

关键点:

  1. proxy.golang.org/list 提供所有已知模块的列表
  2. 结合 @latest 端点获取每个模块的最新版本
  3. 使用并发控制避免速率限制
  4. 考虑使用 goproxy.io 等替代代理的增强接口
  5. 定期增量更新而非全量重建索引

这种方法可以高效获取所有Go包的最新版本列表,适合构建包搜索引擎。

回到顶部