5分ごとにWebページから情報をスクレイプするデーモンをHaskellに書いた。Haskell http-conduit Web-Scrapingデーモンがメモリ不足エラーでクラッシュする
デーモンはもともと約50分間正常に実行されましたが、予期せずout of memory (requested 1048576 bytes)
で終了しました。私がそれを走らせるたびに、それは同じ時間の後に亡くなりました。 30秒しかスリープ状態にならず、代わりに8分後に亡くなった。
ウェブサイトをスクラップするコードがメモリが非常に非効率的であることを認識しました(9Mのhtmlを解析している間に約30Mから250Mにスリープしています)ので、これを書き換えて解析しました。問題は修正されたと思って、夜通しデーモンを走らせました。私が目を覚ますと、実際にはその夜よりもメモリが少なくなっていました。私は終わったと思ったが、始まってから約20時間後、同じエラーで墜落した。
私はghcプロファイリングを調べ始めましたが、それを動作させることができませんでした。次にrts optionsを使いこなし始めたところで、-H64m
を設定して、私のプログラムが使用していたよりも大きいデフォルトのヒープサイズを設定しました。また、-Ksize
を使ってスタックの最大サイズを縮めて早くクラッシュさせるかどうかを確認しました。
私が作ったすべての変更にもかかわらず、デーモンは一定の繰り返し回数の後でもクラッシュするようです。解析のメモリをより効率的にすると、この値は高くなりますが、それでもクラッシュします。これは私には分かりません。なぜなら、これらのどれもが私の記憶のすべてを使うことに近づいていないし、スワップスペースもずっと少ないからです。ヒープサイズはデフォルトでは無限に設定されていますが、スタックサイズを縮小しても差はなく、私のulimitはすべてデーモンが使用しているものよりも無制限または大幅に高くなっています。
元のコードでは、html解析のどこかにクラッシュを突き止めましたが、20時間以上かかるため、より効率的なバージョンのメモリでも同じ処理を行っていません。プログラムの特定の部分がクラッシュする前に何十回も繰り返し実行されたために壊れているように見えないため、これが知るのに役立つかどうかはわかりません。
私はこのエラーのためにghc source codeを調べましたが、それは問題の根本ではないと私は考えていないので、非常に役に立たなかったmmapへの呼び出しに失敗したようです。
(編集:コードを書き換え、ポストの最後に移動)
私はHaskellのではかなり新しいので、私は、これは遅延評価やクイックフィックスを持っている何か他のいくつかのいたずらである願っています。そうでなければ、私はアイデアが新鮮です。
私は、FreeBSD 9.1
編集上のGHCのバージョン7.4.2を使用しています:私はどのように問題を処分した静的なHTMLでダウンロードを交換
ので、私はそれを絞り込むましたhttp-conduitを使用します。上記のコードを編集してネットワークコードを追加しました。私がそれをしたので、ハッカーのドキュメントはマネージャーを共有するために言及しています。また、http
については明示的に接続を閉じる必要があると言いますが、私はhttpLbs
のためにそれを行う必要はないと言います。
ここに私のコードです。
import Control.Monad.IO.Class (liftIO)
import qualified Data.Text as T
import qualified Data.ByteString.Lazy as BL
import Text.Regex.PCRE
import Network.HTTP.Conduit
main :: IO()
main = do
manager <- newManager def
daemonLoop manager
daemonLoop :: Manager -> IO()
daemonLoop manager = do
rows <- scrapeWebpage manager
putStrLn $ "number of rows parsed: " ++ (show $ length rows)
doSleep
daemonLoop manager
scrapeWebpage :: Manager -> IO [[BL.ByteString]]
scrapeWebpage manager = do
putStrLn "before makeRequest"
html <- makeRequest manager
-- Force evaluation of html.
putStrLn $ "html length: " ++ (show $ BL.length html)
putStrLn "after makeRequest"
-- Breaks ~10M html table into 2d list of bytestrings.
-- Max memory usage is about 45M, which is about 15M more than when sleeping.
return $ map tail $ html =~ pattern
where
pattern :: BL.ByteString
pattern = BL.concat $ replicate 12 "<td[^>]*>([^<]+)</td>\\s*"
makeRequest :: Manager -> IO BL.ByteString
makeRequest manager = runResourceT $ do
defReq <- parseUrl url
let request = urlEncodedBody params $ defReq
-- Don't throw errors for bad statuses.
{ checkStatus = \_ _ -> Nothing
-- 1 minute.
, responseTimeout = Just 60000000
}
response <- httpLbs request manager
return $ responseBody response
と、それは出力です:正規表現の計算退治
before makeRequest
html length: 1555212
after makeRequest
number of rows parsed: 3608
...
before makeRequest
html length: 1555212
after makeRequest
bannerstalkerd: out of memory (requested 2097152 bytes)
は、問題を修正しましたが、エラーがネットワーキング後と正規表現の間に起こると思われ、おそらく私は何かをhttp-conduitで間違っています。何か案は?
Could not find module `Network.HTTP.Conduit'
Perhaps you haven't installed the profiling libraries for package `http-conduit-1.8.9'?
は確かに、私はhttp-conduit
のためのライブラリをプロファイリングインストールしていないと私は方法がわからない:。また
、私はプロファイリングでコンパイルしようと、私はこのエラーを取得する有効
DB全体をレイジーテキストファイルに置き換えて、実際にdbであるかどうか確認できますか? –
私は実際にデータベース全体を削除しましたが、それでも同じ問題があります。私はそれを反映するために投稿を編集します。 – nejstastnejsistene
ダウンロード部分を 'let page =" " –