Webcrawler che utilizza un sacco di memoria

Ciao a tutti,
ho fatto uno script in Ruby 1.8 che usa le gemme mechanize, nokogiri e
open-uri e che gira sotto Linux.

Lo script è un webcrawler che raccoglie un sacco di dati aziendali
divisi in categorie. Il mio script gira perfettamente e fa quello che
deve fare ma il problema è che dopo 6-8 ore diventa enorme (1gb circa)
causando il crash del serverino che uso.

Ogni 10 risultati ottenuti li salvo in un file che al massimo raggiunge
10mb altrimenti uso un altro file svuotando il buffer relativo.

Il risultato attuale è tra le altre cose già frutto di un ottimizzazione
in quanto prima di seguire le istruzioni del post seguente soffriva di
questi problemi già dopo 4 ore.

Qualcuno potrebbe consigliarmi per ottimizzare la gestione della memoria
ottimizzando lo script? C’è un metodo serio, un IDE o qualcosa del
genere per fare un Debug efficiente?

Ciao Grazie
Luca

Tempo fa (molto tempo fa) ho avuto un problema simile - ma in scala
ridotta - era Nokogiri che a causa di memory leaks mangiava pi del
dovuto…

PS: non molto, ma spero possa aiutarti

Il 17 novembre 2011 17:12, Lucas P. [email protected] ha scritto:

Qualcuno potrebbe consigliarmi per ottimizzare la gestione della memoria
ottimizzando lo script? C’ un metodo serio, un IDE o qualcosa del
genere per fare un Debug efficiente?

Puoi individuare il memory leak usando memprof[1].

Ciao
Flavio

[1] GitHub - ice799/memprof: A Ruby gem for memory profiling

2011/11/17 Lucas P. [email protected]

Lo script un webcrawler che raccoglie un sacco di dati aziendali
divisi in categorie. Il mio script gira perfettamente e fa quello che
deve fare ma il problema che dopo 6-8 ore diventa enorme (1gb circa)
causando il crash del serverino che uso.

Ogni 10 risultati ottenuti li salvo in un file che al massimo raggiunge
10mb altrimenti uso un altro file svuotando il buffer relativo.

posso rispondere con una domanda? … deve per forza essere un processo
‘monolitico’?

in casi simili quello che ho fatto e’ stato suddividere il lavoro in
unita’
piu’ piccole (e.g. ogni job fa il crawl di 10 URL) in modo che al
termine
di ogni unita di lavoro il programma che la esegue esca (liberando la
memoria.)

La divisione del lavoro in unita’ puo’ essere “statica” se sai a priori
quali sono tutte le attivita da fare (e.g. se conosci le URL da navigare
e
non segui i link interni a queste); in questo caso puoi direttamente
scrivere un programma che lancia tutti i job che ti servono. Altre volte
invece ti serve maggiore sofisticazione nella gestione delle unita di
lavoro (e.g. se non sai quante saranno in totale le URL da navigare o le
scopri strada facendo), in questi casi potresti guidare il sistema con
una
coda di lavoro in cui metti le url da navigare (mano a mano chele
scopri) e
da cui i job estraggono le url da scaricare.

ciao,
Luca

Luca M. wrote in post #1032377:

2011/11/17 Lucas P. [email protected]

posso rispondere con una domanda? … deve per forza essere un processo
‘monolitico’?

Ciao Luca e ciao a tutti gli altri,
grazie a tutti dei consigli e dei suggerimenti.

Ho provato ad utlizzare memprof come da suggerimenti ma su un Ubuntu e
su un debian dava degli errori. Però guardando come veniva utilizzato
GC.start negli esempi di utilizzo di memprof che ho trovato ho pensato
di aggiungere questa istruzione nei punti critici e ora lo script dopo
ben 7 ore di attività pesa 127MB, quasi 1/10. Quindi direi che sono
sulla buona strada, ottima direi.

Per quanto riguarda il suggerimento di Luca per il discorso dei thread
hai ragione sarebbe stata la soluzione migliore ma non sono ancora molto
pratico di Ruby e mi è riuscito più facile scrivere lo script in maniera
monolitica.

Però ora che lo sto ottimizzando potrei pensare ad implementare il tuo
suggerimento.

Intanto grazie a tutti.
Gentilissimi.
Ciao
Luca

2011/11/17 Lucas P. [email protected]

Per quanto riguarda il suggerimento di Luca per il discorso dei thread
hai ragione sarebbe stata la soluzione migliore ma non sono ancora molto
pratico di Ruby e mi riuscito pi facile scrivere lo script in maniera
monolitica.

eheh mi sono espresso male, io non stavo parlando di passare ad usare
dei
thread ma proprio a spezzare il problema in parti eseguite da processi
separati

ciao,
Luca

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 18/11/2011 08:34, Luca M. wrote:

thread ma proprio a spezzare il problema in parti eseguite da processi
separati

ciao, Luca

un processo IO-bound, potresti voler essere event-driven.
Se lo fai dovrai cambiare il modo in cui pensi:

Purtroppo per Mechanize bloccante quindi devi avere Ruby 1.9 con Fiber:

E provare a seguire i loro consigli:
http://comments.gmane.org/gmane.comp.lang.ruby.modules.mechanize.user/530

Antonio B.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)

iQEcBAEBAgAGBQJOx7ieAAoJELgLplTPZUF9nL4H/A1m5CfPzWD5q2aj+fgvmSXX
ij2KxdYsZswnw0hw3OxMV8uoLi9u8lkJj/ALUyxERZi21JMKjW0tbu54U14PMbx7
eE2/O/g/oS1njUILSPUqUdUJ+6MFexsZZoaP6CQIF+E8RE3Lx16AvtpettFOc4cI
Sv3r27Fsck3rhgMGcJUyX8e4theShnuOFLCpkwWjV8vJHzOLqIM5mPOdJp/H2dDV
gJ3YAPwDNFFXHFAU5KIFTStplXJrFlLae4jPhyiYpyHL16pBG9qY9tag/RMrksdQ
fAyncb8jQbPY6n02eoD03RwL8/erBDX/IdG0nX2rASgHEzNws0NuNLbrHk1NEI4=
=FvCb
-----END PGP SIGNATURE-----

2011/11/18 Luca M. [email protected]:

thread ma proprio a spezzare il problema in parti eseguite da processi
separati

io sono con Luca, usa i processi e vivi felice :slight_smile:

Se hai una cosa tipo

url = UrlQueue.get_next_url()
crawl_url(url)

non dovrebbe essere troppo difficile wrapparla in una funziona e
metterla dentro un fork(), ti servono solo un paio di punti di
sincronizzazione (job queue, set “in processing”).

Ma fossi in te proverei a farlo con Resque[1], che gestisce
esattamente le problematiche che hai/avrai tu (processi appesi, o che
abusano di memoria, o fetch falliti da ripetere) usando i processi e
facendoti fare davvero poco sforzo. L’unica cosa in pi installarti
redis, ma molto semplice.

[1]GitHub - defunkt/resque: Moved to resque/resque


twitter: @riffraff
blog (en, it): www.riffraff.info riffraff.blogsome.com
work: cascaad.com circleme.com