2014-04-21 6 views
2

키워드가 웹 페이지에 있는지 확인하는 프로그램이 있습니다. 그러나 1000-3000 개의 URL을 확인한 후 중단됩니다. 출력이없고 종료되지 않고 tcp 연결 수가 0입니다. 왜 새로운 연결이 없는지 나는 모른다.약 2000 개의 URL을 확인한 후 웹 크롤러가 멈춤

디버깅 방법에 대해 조언을 해 주시겠습니까?

type requestReturn struct {  
    url string  
    status bool 
} 

var timeout = time.Duration(800 * time.Millisecond)  

func checkUrls(urls []string, kws string, threadLimit int) []string {  
    limitChan := make(chan int, threadLimit)  
    ok := make(chan requestReturn, 1)  
    var result []string  
    i := 0  
    for ; i < threadLimit; i++ {  
     go func(u string) {  
      request(u, limitChan, ok, kws)  
     }(urls[i])  
    }  
    for o := range ok {  
     if o.status {  
      result = append(result, o.url)  
      log.Printf("success %s,remain %d", o.url, len(urls)-i)  
     } else {  
      log.Printf("fail %s,remain %d", o.url, len(urls)-i)  
     }  
     if i < len(urls) {  
      go func(u string) {  
       request(u, limitChan, ok, kws)  
      }(urls[i])  
      i++  
     }  
    }  
    close(limitChan)  
    return result  
}  

func dialTimeout(network, addr string) (net.Conn, error) {  
    return net.DialTimeout(network, addr, timeout)  
}  

func request(url string, threadLimit chan int, ok chan requestReturn, kws string) {  
    threadLimit <- 1  
    log.Printf("%s, start...", url)  
    //startTime := time.Now().UnixNano()  
    rr := requestReturn{url: url}  

    transport := http.Transport{  
     Dial:    dialTimeout,  
     DisableKeepAlives: true,  
    }  

    client := http.Client{  
     Transport: &transport,  
     Timeout: time.Duration(15 * time.Second),  
    }  

    resp, e := client.Get(url)  
    if e != nil {  
     log.Printf("%q", e)  
     rr.status = false  
     return  
    }  

    if resp.StatusCode == 200 {  
     body, err := ioutil.ReadAll(resp.Body)  
     if err != nil {  
      log.Printf("%q", err)  
      rr.status = false  
      return  
     }  

     content := bytes.NewBuffer(body).String()  

     matched, err1 := regexp.MatchString(kws, content)  
     if err1 != nil {  
      log.Printf("%q", err1)  
      rr.status = false  
     } else if matched {  
      rr.status = true  
      log.Println(rr.url)  
     } else {  
      rr.status = false  
     }  
    } else {  
     rr.status = false  
    }  

    defer (func() {  
     resp.Body.Close()  
     ok <- rr  
     //processed := float32(time.Now().UnixNano()-startTime)/1e9  
     //log.Printf("%s, status:%t,time:%.3fs", rr.url, rr.status, processed)  
     <-threadLimit  
    })()  
} 
+0

재생은 형식화 된 컴파일 가능한 코드를 게시합니다. – OneOfOne

+1

전체 코드는 다음과 같습니다. http : //www.mediafire.com/view/p888clprdjkvlis/checkLinks.go –

+0

언제든지 Windows에서이 작업을 수행 할 수 있습니까? 5 분 안에 3000 개가 넘는 소켓을 만들면 네트워크 연결이 끊어지는 Windows 사용 경험이 있습니다. –

답변

3

이 코드에는 두 가지 형태의 동시성 제어가 사용되는 것으로 보입니다. 둘 다 문제가있는 것 같습니다. 당신은이 세마포어로 사용되는 것처럼 보이는, limitChan있어

( request는 시작에 값을 전송하고, 그 함수에 defer에 값을받습니다). 그러나 checkUrlsthreadLimit 개의 골 루틴을 한 번에 실행하고 있습니다 (그 번호를 먼저 생성하고 ok 채널에서 그 결과를보고 할 때만 더 산란 시킴). 동시성을 제한하기 위해 이들 중 하나만 필요합니다.

defer이 설정되는 방식으로 인해 두 방법 모두 실패합니다. request. 전에 발생하는 return 문이 여러 개 있기 때문에 에 슬롯을 비우지 않고도 결과를 ok 채널로 보내지 않고도 함수를 완료 할 수 있습니다. 충분한 수의 오류가 발생하면 checkUrls은 새 goroutines을 생성하는 것을 멈추게되고 중단을 보게됩니다.

수정 프로그램은 defer 문을 return 문 앞에 삽입하여 항상 실행되도록합니다. 다음과 같은 것 :

func request(url string, threadLimit chan int, ok chan requestReturn, kws string) { 
    threadLimit <- 1 
    rr := requestReturn{url: url} 
    var resp *http.Response 
    defer func() { 
     if resp != nil { 
      resp.Body.Close() 
     } 
     ok <- rr 
     <-threadLimit 
    }() 
    ... 
} 
+0

James, 귀하의 의견에 감사드립니다! –

관련 문제