我正在尝试学习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)
}
答案 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++
}