Haskell http-conduit web-scraping守护进程因内存不足错误而崩溃

nej*_*ene 8 memory haskell conduit

我在Haskell编写了一个守护进程,每隔5分钟从网页上抓取一次信息.

这个守护进程原本可以运行大约50分钟,但后来意外地死了out of memory (requested 1048576 bytes).每次我跑它都会在相同的时间后死亡.将它设置为只睡30秒,它会在8分钟后死亡.

我意识到刮掉网站的代码非常低效(在解析9M的html时,从睡眠时的大约30M到250M),所以我重新编写了它,现在它在解析时只使用了大约15M.认为这个问题已经解决了,我一夜之间就运行了守护进程,当我醒来的时候实际上使用的内存比当晚少.我以为我已经完成了,但是在它开始大约20个小时后,它已经因同样的错误而崩溃了.

我开始研究ghc分析,但我无法让它工作.接下来我开始搞乱rts选项,我尝试设置-H64m默认堆大小比我的程序使用的大,并且还使用-Ksize缩小堆栈的最大大小以查看是否会使它更快崩溃.

尽管我做了很多改变,但守护程序在经过多次迭代后似乎仍然会崩溃.使解析更具内存效率使这个值更高,但它仍然崩溃.这对我来说没有意义,因为这些都没有运行甚至接近使用我的所有内存,更不用说交换空间了.默认情况下,堆大小应该是无限制的,缩小堆栈大小并没有什么区别,并且我的所有ulimits都是无限的或远远高于守护进程使用的.

在原始代码中,我将崩溃指向了html解析中的某个地方,但我没有对更高效的内存版本做同样的事情,因为运行20个小时需要很长时间.我不知道这是否有用甚至是有用的,因为它似乎没有破坏程序的任何特定部分,因为它在崩溃之前成功运行了几十次迭代.

出于想法,我甚至查看了ghc源代码中的这个错误,它似乎是对mmap的失败调用,这对我没有多大帮助,因为我认为这不是问题的根源.

(编辑:代码重写并移至帖子末尾)

我是Haskell的新手,所以我希望这是懒惰评估的一些怪癖或其他快速修复的东西.否则,我是新鲜的想法.

我在FreeBsd 9.1上使用GHC版本7.4.2

编辑:

用静态html替换下载摆脱了问题,所以我把它缩小到我如何使用http-conduit.我编辑了上面的代码以包含我的网络代码.hackage docs提到分享经理所以我已经这样做了.它还说,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
Run Code Online (Sandbox Code Playgroud)

它的输出:

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)
Run Code Online (Sandbox Code Playgroud)

摆脱正则表达式计算解决了问题,但似乎错误发生在网络之后和正则表达式期间,可能是因为我在使用http-conduit时出错了.有任何想法吗?

此外,当我尝试编译并启用性能分析时,我收到此错误:

Could not find module `Network.HTTP.Conduit'
Perhaps you haven't installed the profiling libraries for package `http-conduit-1.8.9'?
Run Code Online (Sandbox Code Playgroud)

实际上,我没有安装分析库http-conduit,我不知道如何.

nej*_*ene 3

我最终解决了我自己的问题。这似乎是 FreeBSD 上的一个 GHC bug。我提交了一份错误报告并切换到 Linux,现在它在过去几天运行得非常完美。