2017-12-15 15 views
1

基本的には、goroutinesを使用して同時サイトマップクローラを作成しようとしています。一つのサイトマップは今のところなどそのチャンネルのデータを受信して​​いるゴルーチンからオブジェクトをチャンネルに追加するにはどうすればよいですか?

を他のサイトマップへのリンクを含めることができ、複数のサイトマップへのリンクを含めることができ、これは私のデザインです:

worker: 
    - receives url from channel 
    - processesUrl(url) 
processUrl: 
    for each link in lookup(url): 
     - if link is sitemap: 
       channel <- url 
      else: 
       print(url) 
main: 
    - create 10 workers 
    - chanel <- root url 

問題は、労働者がより新しいURLを受け入れないということですprocessUrl()が終了するまでのチャンネル。processUrlは、チャンネルにURLを追加する場合、ワーカーがチャンネルから新しいURLを受け入れるまで終了しません。どのコンカレント・デザインを使用して、チャネルなしでビジー待ちなしで、またはchannel <- urlを待たずにURLをタスク・キューに追加できますか?それは場合に役立ちます

は、ここで実際のコードです:チャネルがバッファリングされていないときのチャンネルに

func (c *SitemapCrawler) worker() { 
    for { 
     select { 
     case url := <-urlChan: 
      fmt.Println(url) 
      c.crawlSitemap(url) 
     } 
    } 
} 
func crawlUrl(url string) { 
    defer crawlWg.Done() 
    crawler := NewCrawler(url) 
    for i := 0; i < MaxCrawlRate*20; i++ { 
     go crawler.worker() 
    } 
    crawler.getSitemaps() 
    pretty.Println(crawler.sitemaps) 
    crawler.crawlSitemaps() 
} 
func (c SitemapCrawler) crawlSitemap(url string) { 
    c.limiter.Take() 
    resp, err := MakeRequest(url) 
    if err != nil || resp.StatusCode != 200 { 
     crawlWg.Done() 
     return 
    } 
    var resp_txt []byte 
    if strings.Contains(resp.Header.Get("Content-Type"), "html") { 
     crawlWg.Done() 
     return 
    } else if strings.Contains(url, ".gz") || resp.Header.Get("Content-Encoding") == "gzip" { 
     reader, err := gzip.NewReader(resp.Body) 
     if err != nil { 
      crawlWg.Done() 
      panic(err) 
     } else { 
      resp_txt, err = ioutil.ReadAll(reader) 
      if err != nil { 
       crawlWg.Done() 
       panic(err) 
      } 
     } 
     reader.Close() 
    } else { 
     resp_txt, err = ioutil.ReadAll(resp.Body) 
     if err != nil { 
      //panic(err) 
      crawlWg.Done() 
      return 
     } 
    } 
    io.Copy(ioutil.Discard, resp.Body) 
    resp.Body.Close() 

    d, err := libxml2.ParseString(string(resp_txt)) 
    if err != nil { 
     crawlWg.Done() 
     return 
    } 
    results, err := d.Find("//*[contains(local-name(), 'loc')]") 
    if err != nil { 
     crawlWg.Done() 
     return 
    } 
    locs := results.NodeList() 
    printLock.Lock() 
    for i := 0; i < len(locs); i++ { 
     newUrl := locs[i].TextContent() 
     if strings.Contains(newUrl, ".xml") { 
      crawlWg.Add(1) 
      //go c.crawlSitemap(newUrl) 
      urlChan <- newUrl 
     } else { 
      fmt.Println(newUrl) 
     } 
    } 
    printLock.Unlock() 

    crawlWg.Done() 
} 
+2

あなたは大きなバッファ – Flimzy

+3

'行くFUNC(){チャンネル< - URL}を使用する必要があるような音クロール・スペース内のURLの不特定多数を持っているのであれば()' – Peter

答えて

0

書き込み操作がブロックされています。このチャンネルは、しかし、一杯になると、

urlChan := make(chan string, len(allUrls)) 

操作が再びブロックします書き込み:

は、バッファリングされたチャネルを作成します。

スイッチを使用することもできます。書き込みが「失敗」と、それはすぐに別のゴーチャンネルに書き込みイベントを入れて行うついに

select { 
case urlChan <- url: 
    fmt.Println("received message") 
case <-time.After(5 * time.Second): 
    fmt.Println("timed out") 
} 

または、次のチャンネルへの書き込みに

select { 
case urlChan <- url: 
    fmt.Println("received message") 
default: 
    fmt.Println("no activity") 
} 

はタイムアウトを持っているために、デフォルトして分類されます

func write() { 
    urlChan <- url 
} 

go write() 
+0

をwoul、 d私はちょうどチャネルのために任意に高いバッファを設定するか、#3私の唯一のオプションですか? –

+0

あなたは大きなバッファを使うことができますが、これはかなりハッキリです。 #3それが好ましいと思われる。別の方法として、全体のチャネル・キュー・アプローチを控え、見つけたURLをデータベースに書き込むこともできます(redisのようなものかもしれません)。各ワーカーはデータベースに未完了のURLを問い合わせることができます。これには、アプリケーションが終了したときのすべての進捗状況を失わないという利点があります。別のオプションは、ミューテックススライスを回すことです。これはかなり複雑に思えます。 –

関連する問題