多个Http.Get随机挂起

时间:2018-10-05 15:00:44

标签: go

我正在尝试学习Golang,并进行了一个简单的项目来调用所有craigslist城市并查询它们以进行特定搜索。在下面的代码中,我删除了listingmap中的所有链接,但是那里有超过400个链接。因此,循环相当大。我认为这是将我所学的知识应用到应用程序中的一个很好的测试,但是我遇到了一个奇怪的问题。

在大多数情况下,大多数Http.Get()都不会从服务器获得任何响应,而其他情况下,它却可以毫无问题地获得所有响应。因此,我开始添加打印品,以显示出有多少错误,我们已恢复并成功解决了多少错误。同样,在此程序运行时,它将随机挂起并且永远不会响应。该程序不会冻结,但是站点只是坐在那里尝试加载,终端没有任何活动。

我通过延迟恢复后的清理来确保响应主体已关闭,但它似乎仍然无法正常工作。有什么东西可能会让我想念的人跳出来吗?

预先感谢大家!

package main

import (
    "fmt"
    "net/http"
    "html/template"
    "io/ioutil"
    "encoding/xml"
    "sync"
    )

var wg sync.WaitGroup

var locationMap = map[string]string {"https://auburn.craigslist.org/": "auburn "...}

var totalRecovers int = 0
var successfulReads int = 0

type Listings struct {
    Links []string `xml:"item>link"`
    Titles []string `xml:"item>title"`
    Descriptions []string `xml:"item>description"`
    Dates []string `xml:"item>date"`
}

type Listing struct {
    Title string
    Description string
    Date string
}

type ListAggPage struct {
        Title string
        Listings map[string]Listing
        SearchRequest string
}

func cleanUp(link string) {
    defer wg.Done()
    if r:= recover(); r!= nil {
        totalRecovers++
//      recoverMap <- link
    }
}

func cityRoutine(c chan Listings, link string) {
    defer cleanUp(link)

    var i Listings
    address := link + "search/sss?format=rss&query=motorhome"
    resp, rErr := http.Get(address)
    if(rErr != nil) {
        fmt.Println("Fatal error has occurs while getting response.")
        fmt.Println(rErr);
    }

    bytes, bErr := ioutil.ReadAll(resp.Body)
    if(bErr != nil) {
        fmt.Println("Fatal error has occurs while getting bytes.")
        fmt.Println(bErr);
    }
    xml.Unmarshal(bytes, &i)
    resp.Body.Close()
    c <- i
    successfulReads++
}

func listingAggHandler(w http.ResponseWriter, r *http.Request) {
    queue := make(chan Listings, 99999)
    listing_map := make(map[string]Listing)

    for key, _ := range locationMap {
        wg.Add(1)
        go cityRoutine(queue, key)
    }

    wg.Wait()
    close(queue)

    for elem := range queue { 
        for index, _ := range elem.Links {
        listing_map[elem.Links[index]] = Listing{elem.Titles[index * 2], elem.Descriptions[index], elem.Dates[index]}
        }
    }

    p := ListAggPage{Title: "Craigslist Aggregator", Listings: listing_map}
    t, _ := template.ParseFiles("basictemplating.html")
    fmt.Println(t.Execute(w, p))

    fmt.Println("Successfully loaded: ", successfulReads)       
    fmt.Println("Recovered from: ", totalRecovers)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "<h1>Whoa, Go is neat!</h1>")
}

func main() {
    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/agg/", listingAggHandler)
    http.ListenAndServe(":8000", nil) 
}

2 个答案:

答案 0 :(得分:1)

我在查找有关此内容的golang邮件列表讨论时遇到了麻烦,但是您通常不希望打开数百个请求。这里有一些信息:How Can I Effectively 'Max Out' Concurrent HTTP Requests?

Craigslist也可能只是限制您的速率。无论哪种方式,我建议限制在大约20个并发请求左右,这是对listingAggHandler的快速更新。

queue := make(chan Listings, 99999)
listing_map := make(map[string]Listing)

request_queue := make(chan string)
for i := 0; i < 20; i++ {
    go func() {
        for {
            key := <- request_queue
            cityRoutine(queue, key)                
        }
    }()
}

for key, _ := range locationMap {
    wg.Add(1)
    request_queue <- key
}

wg.Wait()
close(request_queue)
close(queue)

应用程序仍然应该非常快。我也同意您的问题上的其他评论。还将尝试避免在全球范围内投入过多。

您还可以通过仅使用请求池中的等待组来简化我的更改,并使每个goroutine自己清理并减少等待组。那将限制某些全球范围。

答案 1 :(得分:0)

因此,我遵循了所有人的建议,并且似乎可以解决我的问题,因此,我非常感谢。我最终像许多建议的那样删除了全局WaitGroup,并将其作为参数(指针)传递来清理代码。至于以前的错误问题,它必须已经使maxm提到的并发HTTP请求达到最大。在每20个搜索之间添加一次等待后,我再也没有看到任何错误。该程序的运行速度比我希望的慢一些,但是出于学习目的,这很有帮助。

下面是需要的代码的主要更改。

        counter := 0
    for key, _ := range locationMap {
        if(counter >= 20) {
            wg.Wait()
            counter = 0
        }
        wg.Add(1)
        frmtSearch := key + "search/sss?format=rss&query=" + strings.Replace(p.SearchRequest, " ", "%20", -1)
        go cityRoutine(queue, frmtSearch, &wg)
        counter++
    }