Golang递归爬虫导致阻塞状态

问题描述 投票:0回答:1

我正在尝试在 Go 中创建一个爬虫来生成 X 个 goroutine。目前,我只生成一个 goroutine (workers=1),并且我正在使用一个通道来发送/读取它。期望的结果是产生一定数量的工作人员,这些工作人员从 chan 读取作业,并且每个作业可以在不阻塞 goroutine 的情况下将更多作业推送到 chan。

但是由于某种原因,现在 goroutine 被阻塞并且不处理递归链接。可能是什么原因?我该如何解决这个问题?

const maxDepth = 1

type job struct {
    URL   string
    Depth int
}

type Crawler struct {
    jobs             chan job
    workers          int
    db               db.DB
    timeout          int
    imagesFolderName string
    visited          *sync.Map
    wg               *sync.WaitGroup
}

func NewCrawler(db db.DB, workers int, timeout int, imagesFolderName string) *Crawler {
    return &Crawler{
        jobs:             make(chan job),
        workers:          workers,
        db:               db,
        timeout:          timeout,
        imagesFolderName: imagesFolderName,
        visited:          &sync.Map{},
        wg:               &sync.WaitGroup{},
    }
}

func (c *Crawler) enqueue(j job) {
    c.wg.Add(1)
    c.jobs <- j
}

func (c *Crawler) Start(startingLinks []string) {
    if err := os.MkdirAll("apps/imagecrawler/"+c.imagesFolderName, os.ModePerm); err != nil {
        fmt.Printf("Error creating image directory: %v\n", err)
        os.Exit(1)
    }

    for i := 0; i < c.workers; i++ {
        go func() {
            for j := range c.jobs {
                c.runJob(j)
                c.wg.Done()
            }
        }()
    }

    for _, link := range startingLinks {
        c.enqueue(job{URL: link, Depth: 0})
    }
    c.wg.Wait()
    close(c.jobs)
}

func (c *Crawler) runJob(j job) {
    _, alreadyVisited := c.visited.LoadOrStore(j.URL, true)
    if alreadyVisited {
        return
    }

    pageCtx, pageCancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(c.timeout))
    fmt.Println("Crawling URL", j.URL, "Depth:", j.Depth)
    resp, err := http.Get(j.URL)
    if err != nil {
        fmt.Println("Fetching error:", err)
        resp.Body.Close()
        pageCancel()
        return
    }
    b, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Reading body: ", err)
        resp.Body.Close()
        pageCancel()
        return
    }
    resp.Body.Close()

    if j.Depth >= maxDepth {
        pageCancel()
        return
    }

    links, err := extracter.ExtractLinks(pageCtx, b, j.URL)
    if err != nil {
        fmt.Println("ExtractLinks Err", err)
        pageCancel()
        return
    }

    for _, link := range links {
        c.enqueue(job{URL: link, Depth: j.Depth + 1})
    }

    pageCancel()
}
go concurrency web-crawler
1个回答
0
投票

解决方案实际上是将递归链接放入一个单独的 goroutine 中,这部分解决了问题。尽管如此,这还是创建/运行了更多 goroutine。我希望在整个爬行过程中只有 X 个创建并运行 goroutine。有替代解决方案吗?

func (c *Crawler) runJob(j job) {
    // ...Same stuff

    for _, link := range links {
        c.wg.Add(1)
        --> go func(link string) {
            c.jobs <- job{URL: link, Depth: j.Depth + 1}
        }(link)
    }
    pageCancel()
}
© www.soinside.com 2019 - 2024. All rights reserved.