Я написал демона в Haskell, который сбрасывает информацию с веб-страницы каждые 5 минут.Haskell http-conduit web-scraping daemon сбой с ошибкой памяти
Демон первоначально работал нормально около 50 минут, но затем он неожиданно умер с out of memory (requested 1048576 bytes)
. Каждый раз, когда я его запускал, он умирал после такого же количества времени. Устанавливая его только на 30 секунд, он вместо этого умер через 8 минут.
Я понял, что код для очистки веб-сайта был невероятно неэффективным (начиная с 30 М в то время как спал до 250 М при разборе 9М html), поэтому я переписал его так, что теперь он использует только около 15 М при разборе. Размышление о проблеме было исправлено, я запустил демона в одночасье, и когда я проснулся, на самом деле он использовал меньше памяти, чем в ту ночь. Я думал, что я закончил, но примерно через 20 часов после его начала он потерпел крах с той же ошибкой.
Я начал изучать профилирование ghc, но я не смог заставить это работать. Затем я начал messing с rts options, и я попытался установить -H64m
, чтобы установить размер кучи по умолчанию, который будет больше, чем моя программа использовала, а также используя -Ksize
, чтобы уменьшить максимальный размер стека, чтобы увидеть, приведет ли это к сбою раньше.
Несмотря на все изменения, которые я сделал, демон по-прежнему терпит крах после постоянного количества итераций. Усвоение синтаксического анализа большей эффективностью памяти сделало это значение выше, но оно все еще падает. Это не имеет смысла для меня, потому что ни один из них не работает, даже приблизился к использованию всей моей памяти, а тем более к обмену местами. Размер кучи, как предполагается, по умолчанию не ограничен, сокращение размера стека не имеет значения, и все мои ulimits либо неограниченны, либо значительно превышают то, что использует демон.
В исходном коде я точно указал, что вы столкнулись где-то в синтаксическом разборе html, но я не сделал то же самое для более эффективной версии памяти, так как 20 часов так долго запускаются. Я не знаю, было бы это полезно знать, потому что это не похоже на то, что какая-то конкретная часть программы нарушена, потому что она успешно запускается для десятков итераций перед сбоем.
Из-за идей я даже просмотрел ghc source code для этой ошибки, и это, кажется, неудачный вызов mmap, что было не очень полезно для меня, потому что я предполагаю, что это не корень проблемы.
(Edit: код переписан и переехал в конец поста)
Я довольно новый на Haskell, так что я надеюсь, что это какой-то бзик отложенной оценки или что-то другое, что имеет быстрое решение. В противном случае, я свеж от идей.
Я использую GHC версию 7.4.2 на FREEBSD 9,1
Edit:
Замену загрузки со статической 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
и его вывод:
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 =" "' –