2017-06-15 6 views
0

多くの情報源(数百万のレコード)からクローラにいくつかのapiを実行していますが、問題がメモリ不足に陥っています。私はgoogledといくつかのリソースを見つけましたが、それは私の問題を解決しません。ここに私の問題クローラ数百万回のレコードをヒープする場合

を解決しませんでした

similar questionは私のサンプルコードです:

function getContent() { 

    let d = q.defer(); 

    let urls = []; 

    array.forEach(function(mang, index) { 
     for (let i = 1; i <= 600000; i++) { 
      urls.push(function (callback) { 
       setTimeout(function() { 
        let link = 'http://something.com/' + i; 
        let x = link; 

        let options = { 
         url: link, 
         headers: { 
          'User-Agent': 'something' 
         } 
        }; 

        function callback1(error, response, html) { 
         if (!error) { 
          let $ = cheerio.load(html); 
          let tag_name = $('h1').text(); 
          tag_name = tag_name.trim(); 
          let tag_content = $('#content-holder').find('div').text(); 
           let tagObject = new Object(); 

           tagObject.tag_name = tag_name; 
           tagObject.tag_content = tag_content; 
           tagObject.tag_number = i; 

           tagArray.push(tagObject); 

            for (let v = 0; v < tagArray.length; v++) { 
             //console.log("INSERT INTO `tags` (tag_name, content, story_id, tag_number) SELECT * FROM (SELECT " + "'" + tagArray[v].tag_name + "'" + "," + "'" + tagArray[v].tag_content + "','" + array[c].story_id + "','" + tagArray[v].tag_number + "' as ChapName) AS tmp WHERE NOT EXISTS (SELECT `tag_name` FROM `tags` WHERE `tag_name`=" + "'" + tagArray[v].tag_name + "'" + ") LIMIT 1"); 
             db.query("INSERT INTO `tags` (tag_name, content) " + 
              "SELECT * FROM (SELECT " + "'" + tagArray[v].tag_name + "'" + "," + "'" + tagArray[v].tag_content + "','" + "' as TagName) AS tmp " + 
              "WHERE NOT EXISTS (SELECT `tag_name` FROM `tags` WHERE `tag_name`=" + "'" + tagArray[v].tag_name + "'" + ") " + 
              "LIMIT 1", (err) => { 
              if (err) { 
               console.log(err); 
              } 
             }); 
            } 
            urls = null; 
            global.gc(); 

           console.log("Program is using " + heapUsed + " bytes of Heap.") 

         } 
        } 

        request(options, callback1); 
        callback(null, x); 
       }, 15000); 
      }); 
     } 
    }); 

    d.resolve(urls); 
    return d.promise; 
} 

getContent() 
    .then(function (data) { 
     let tasks = data; 
     console.log("start data"); 
     async.parallelLimit(tasks, 10,() => { 
      console.log("DONE "); 
     }); 
    }) 

私はglobal.gc()関数を使用しようとしたが、ない効果的

+1

あなたは最小限の実行可能な質問を作成する必要があります。あなたの仕事を示し、あなたが探していることを説明してください。この質問は、私たちがあなたのアプローチについてあまりにも多くの仮定をする必要があります。 – Paul

+0

@Paul私はいくつかのコード例を挙げて編集しました:( –

+0

答えを追加しましたが、分かりませんが、 'array'には何がありますか?forEachを実行していますが、また、配列の中でアイテムごとにヒープ*に少なくとも120万の関数を追加していることに気づいたでしょうか?コールバック1が一度作成されるかどうかを思い出すことはできませんまたはスコープごとに1回、かなり後者です。 – Paul

答えて

1

ああのようです、私は今あなたの問題を参照してください。 1回のループですべてをメモリー内で実行しようとしています。その方法は、あなたが作成しているそれらの無名関数のそれぞれがヒープに追加されるので、些細な量の作業でも狂ってしまいます。さらに、それほど堅牢ではありません。 450,000回のクロールでネットワークが停止するとどうなりますか?あなたはそれをすべて失い、やり直しますか?

小さなバッチで実行されている仕事を探してください。私はこれまでKueのようなタスクマネージャを使ってきましたが、率直に言えば、あなたの配列を10または25のような適切な数で埋めることから始めてください。一つの方法は、その中のすべてのURLとそれらが正常にクロールされたことを示すフラグ、またはそれらを再度実行する予定がある場合は最後にクロールされた日付です。

次に、クロールされていない(または1週間前と同じように最後にクロールされた)すべてのURLをクエリし、結果を10または25などに制限します。それらを最初にクロールして保存しておきますが、現在使用しているループではなく、async.js#mapまたはPromise.allのようなものを使用しています。

すべてのURLが同じドメインに当てはまる場合は、各リクエストの間に短い時間切れでそれらのリソースを尊重したいと思うかもしれません。

バッチが完了したら、DBに次のバッチを問い合わせて繰り返します。

アーキテクチャによっては、このプログラムをよりシンプルにすること、1つのバッチを取得すること、1つのバッチのクロールを解決することが良いことがあります。その後、cronジョブまたはWindowsサービスとして実行し、5分ごとまたは15分ごとに実行することができます。

今ではモバイルですが、必要に応じて後でラップトップを使用してコード例を紹介します。

関連する問題