2017-10-18 22 views
0

URLを取得し、そこからリンクを抽出し、それぞれを特定の深度まで訪問するクローラを構築しています。特定のサイトにパスのツリーを作成します。"オープンファイルが多すぎます"を処理する最善の方法は何ですか?

私は、このクローラのための並列処理を実装方法は、それが次のように見つかったとして、私は、すぐにそれぞれの新しい見つけられたURLにアクセスしてくださいということです。

func main() { 
    link := "https://example.com" 

    wg := new(sync.WaitGroup) 
    wg.Add(1) 

    q := make(chan string) 
    go deduplicate(q, wg) 
    q <- link 
    wg.Wait() 
} 

func deduplicate(ch chan string, wg *sync.WaitGroup) { 
    for link := range ch { 
     // seen is a global variable that holds all seen URLs 
     if seen[link] { 
      wg.Done() 
      continue 
     } 
     seen[link] = true 
     go crawl(link, ch, wg) 
    } 
} 

func crawl(link string, q chan string, wg *sync.WaitGroup) { 
    // handle the link and create a variable "links" containing the links found inside the page 
    wg.Add(len(links)) 
    for _, l := range links { 
     q <- l} 
    } 
} 

私は上でそれを実行したときにこれが比較的小さなサイトのために正常に動作しますが、どこにでもたくさんのリンクがある大規模なものですが、私はいくつかのリクエストでこれら2つのエラーの1つ、すなわちsocket: too many open filesno such host(ホストは確かにそこにあります)を取得し始めます。

これを処理する最善の方法は何ですか?これらのエラーをチェックして、他のリクエストが完了するまでしばらくの間、実行を一時停止する必要がありますか?または、特定の時間に可能な最大リクエスト数を指定しますか? (正確にコード化する方法がわかりません)

+0

オペレーティングシステムによって制御されるユーザーあたりの開かれたファイルの制限に関連する問題に直面しています。 Linux/Unixを使用している場合は、おそらくulimit -n 4096コマンドを使用して制限を増やすことができます。このコマンドにはしきい値があり、開いているファイルの数を設定することはできません。 ですから、さらにそれを押したい場合は、/ etc/security/limits.confファイルを修正し、ハードとソフトの制限を設定する必要があります。 –

+2

また、enconuterのリンクごとにゴルーチンを開始していますが、それらの多くが特定の時点でゴルーチンの目的を破り、実際にはその処理に時間がかかります。処理を行い、各リンクのために新しいものを開始するのではなく、チャンネルから読み込むために一定数のゴルーチンを試してください。 https://blog.golang.org/pipelines – Topo

+4

か、https://gobyexample.com/worker-poolsのようなパターンをご覧ください。 (あなたの 'WaitGroup'の使用法は非常に奇妙です。各ゴルーチンに1を加えて、各ゴルーチン内から' Done'を延期します)他のものはバグを要求しています) – JimB

答えて

0

エラーsocket: too many open filesで参照されているファイルには、スレッドとソケット(スクラップされているWebページをロードするためのhttp要求)が含まれています。 これを見るquestion

DNSクエリも、ファイルを作成できないために失敗する可能性がありますが、報告されるエラーはno such hostです。

問題の2つの方法で固定することができます。

1) Increase the maximum number of open file handles 
2) Limit the maximum number of concurrent `crawl` calls 

1)は、最も簡単な解決策ですが、あなたは新しいより多くのリンクを持っているウェブサイトを見つけるまで、それが唯一の問題を延期として理想的ではないかもしれません制限する。 Linuxの場合、この制限はulimit -nで設定できます。

2)デザインの問題が多いです。同時に行うことができるHTTP要求の数を制限する必要があります。コードを少し修正しました。最も重要な変更はmaxGoRoutinesです。値を開始するすべてのスクレイピングコールがチャネルに挿入されます。チャネルが一杯になると、チャネルから値が削除されるまで次の呼び出しがブロックされます。スクレイピングコールが終了するたびに、チャネルから値が削除されます。

package main 

import (
    "fmt" 
    "sync" 
    "time" 
) 

func main() { 
    link := "https://example.com" 

    wg := new(sync.WaitGroup) 
    wg.Add(1) 

    q := make(chan string) 
    go deduplicate(q, wg) 
    q <- link 
    fmt.Println("waiting") 
    wg.Wait() 
} 

//This is the maximum number of concurrent scraping calls running 
var MaxCount = 100 
var maxGoRoutines = make(chan struct{}, MaxCount) 

func deduplicate(ch chan string, wg *sync.WaitGroup) { 
    seen := make(map[string]bool) 
    for link := range ch { 
     // seen is a global variable that holds all seen URLs 
     if seen[link] { 
      wg.Done() 
      continue 
     } 
     seen[link] = true 
     wg.Add(1) 
     go crawl(link, ch, wg) 
    } 
} 

func crawl(link string, q chan string, wg *sync.WaitGroup) { 
    //This allows us to know when all the requests are done, so that we can quit 
    defer wg.Done() 

    links := doCrawl(link) 

    for _, l := range links { 
     q <- l 
    } 
} 

func doCrawl(link string) []string { 
    //This limits the maximum number of concurrent scraping requests 
    maxGoRoutines <- struct{}{} 
    defer func() { <-maxGoRoutines }() 

    // handle the link and create a variable "links" containing the links found inside the page 
    time.Sleep(time.Second) 
    return []string{link + "a", link + "b"} 
} 
関連する問題