mirror of https://github.com/scrapy/scrapy.git
commit
e7843d35de
|
|
@ -35,26 +35,32 @@ in Scrapy projects, and a quite difficult one to debug for newcomers.
|
|||
In big projects, the spiders are typically written by different people and some
|
||||
of those spiders could be "leaking" and thus affecting the rest of the other
|
||||
(well-written) spiders when they get to run concurrently, which, in turn,
|
||||
affects the whole crawling process.
|
||||
|
||||
At the same time, it's hard to avoid the reasons that cause these leaks
|
||||
without restricting the power of the framework, so we have decided not to
|
||||
restrict the functionally but provide useful tools for debugging these leaks,
|
||||
which quite often consist in an answer to the question: *which spider is leaking?*.
|
||||
affects the whole crawling process.
|
||||
|
||||
The leak could also come from a custom middleware, pipeline or extension that
|
||||
you have written, if you are not releasing the (previously allocated) resources
|
||||
properly. For example, if you're allocating resources on
|
||||
:signal:`spider_opened` but not releasing them on :signal:`spider_closed`.
|
||||
properly. For example, allocating resources on :signal:`spider_opened`
|
||||
but not releasing them on :signal:`spider_closed` may cause problems if
|
||||
you're running :ref:`multiple spiders per process <run-multiple-spiders>`.
|
||||
|
||||
Too Many Requests?
|
||||
------------------
|
||||
|
||||
By default Scrapy keeps the request queue in memory; it includes
|
||||
:class:`~scrapy.http.Request` objects and all objects
|
||||
referenced in Request attributes (e.g. in :attr:`~scrapy.http.Request.meta`).
|
||||
While not necesserily a leak, this can take a lot of memory. Enabling
|
||||
:ref:`persistent job queue <topics-jobs>` could help keeping memory usage
|
||||
in control.
|
||||
|
||||
.. _topics-leaks-trackrefs:
|
||||
|
||||
Debugging memory leaks with ``trackref``
|
||||
========================================
|
||||
|
||||
``trackref`` is a module provided by Scrapy to debug the most common cases of
|
||||
:mod:`trackref` is a module provided by Scrapy to debug the most common cases of
|
||||
memory leaks. It basically tracks the references to all live Requests,
|
||||
Responses, Item and Selector objects.
|
||||
Responses, Item and Selector objects.
|
||||
|
||||
You can enter the telnet console and inspect how many objects (of the classes
|
||||
mentioned above) are currently alive using the ``prefs()`` function which is an
|
||||
|
|
@ -71,12 +77,10 @@ alias to the :func:`~scrapy.utils.trackref.print_live_refs` function::
|
|||
FormRequest 878 oldest: 7s ago
|
||||
|
||||
As you can see, that report also shows the "age" of the oldest object in each
|
||||
class.
|
||||
|
||||
If you do have leaks, chances are you can figure out which spider is leaking by
|
||||
looking at the oldest request or response. You can get the oldest object of
|
||||
each class using the :func:`~scrapy.utils.trackref.get_oldest` function like
|
||||
this (from the telnet console).
|
||||
class. If you're running multiple spiders per process chances are you can
|
||||
figure out which spider is leaking by looking at the oldest request or response.
|
||||
You can get the oldest object of each class using the
|
||||
:func:`~scrapy.utils.trackref.get_oldest` function (from the telnet console).
|
||||
|
||||
Which objects are tracked?
|
||||
--------------------------
|
||||
|
|
@ -84,17 +88,16 @@ Which objects are tracked?
|
|||
The objects tracked by ``trackrefs`` are all from these classes (and all its
|
||||
subclasses):
|
||||
|
||||
* ``scrapy.http.Request``
|
||||
* ``scrapy.http.Response``
|
||||
* ``scrapy.item.Item``
|
||||
* ``scrapy.selector.Selector``
|
||||
* ``scrapy.spider.Spider``
|
||||
* :class:`scrapy.http.Request`
|
||||
* :class:`scrapy.http.Response`
|
||||
* :class:`scrapy.item.Item`
|
||||
* :class:`scrapy.selector.Selector`
|
||||
* :class:`scrapy.spider.Spider`
|
||||
|
||||
A real example
|
||||
--------------
|
||||
|
||||
Let's see a concrete example of an hypothetical case of memory leaks.
|
||||
|
||||
Suppose we have some spider with a line similar to this one::
|
||||
|
||||
return Request("http://www.somenastyspider.com/product.php?pid=%d" % product_id,
|
||||
|
|
@ -104,7 +107,7 @@ That line is passing a response reference inside a request which effectively
|
|||
ties the response lifetime to the requests' one, and that would definitely
|
||||
cause memory leaks.
|
||||
|
||||
Let's see how we can discover which one is the nasty spider (without knowing it
|
||||
Let's see how we can discover the cause (without knowing it
|
||||
a-priori, of course) by using the ``trackref`` tool.
|
||||
|
||||
After the crawler is running for a few minutes and we notice its memory usage
|
||||
|
|
@ -121,20 +124,21 @@ references::
|
|||
|
||||
The fact that there are so many live responses (and that they're so old) is
|
||||
definitely suspicious, as responses should have a relatively short lifetime
|
||||
compared to Requests. So let's check the oldest response::
|
||||
compared to Requests. The number of responses is similar to the number
|
||||
of requests, so it looks like they are tied in a some way. We can now go
|
||||
and check the code of the spider to discover the nasty line that is
|
||||
generating the leaks (passing response references inside requests).
|
||||
|
||||
Sometimes extra information about live objects can be helpful.
|
||||
Let's check the oldest response::
|
||||
|
||||
>>> from scrapy.utils.trackref import get_oldest
|
||||
>>> r = get_oldest('HtmlResponse')
|
||||
>>> r.url
|
||||
'http://www.somenastyspider.com/product.php?pid=123'
|
||||
|
||||
There it is. By looking at the URL of the oldest response we can see it belongs
|
||||
to the ``somenastyspider.com`` spider. We can now go and check the code of that
|
||||
spider to discover the nasty line that is generating the leaks (passing
|
||||
response references inside requests).
|
||||
|
||||
If you want to iterate over all objects, instead of getting the oldest one, you
|
||||
can use the :func:`iter_all` function::
|
||||
can use the :func:`scrapy.utils.trackref.iter_all` function::
|
||||
|
||||
>>> from scrapy.utils.trackref import iter_all
|
||||
>>> [r.url for r in iter_all('HtmlResponse')]
|
||||
|
|
@ -145,16 +149,15 @@ can use the :func:`iter_all` function::
|
|||
Too many spiders?
|
||||
-----------------
|
||||
|
||||
If your project has too many spiders, the output of ``prefs()`` can be
|
||||
difficult to read. For this reason, that function has a ``ignore`` argument
|
||||
which can be used to ignore a particular class (and all its subclases). For
|
||||
example, using::
|
||||
If your project has too many spiders executed in parallel,
|
||||
the output of :func:`prefs()` can be difficult to read.
|
||||
For this reason, that function has a ``ignore`` argument which can be used to
|
||||
ignore a particular class (and all its subclases). For
|
||||
example, this won't show any live references to spiders::
|
||||
|
||||
>>> from scrapy.spider import Spider
|
||||
>>> prefs(ignore=Spider)
|
||||
|
||||
Won't show any live references to spiders.
|
||||
|
||||
.. module:: scrapy.utils.trackref
|
||||
:synopsis: Track references of live objects
|
||||
|
||||
|
|
@ -198,15 +201,13 @@ leaks, but it only keeps track of the objects that are more likely to cause
|
|||
memory leaks (Requests, Responses, Items, and Selectors). However, there are
|
||||
other cases where the memory leaks could come from other (more or less obscure)
|
||||
objects. If this is your case, and you can't find your leaks using ``trackref``,
|
||||
you still have another resource: the `Guppy library`_.
|
||||
you still have another resource: the `Guppy library`_.
|
||||
|
||||
.. _Guppy library: http://pypi.python.org/pypi/guppy
|
||||
|
||||
If you use ``setuptools``, you can install Guppy with the following command::
|
||||
If you use ``pip``, you can install Guppy with the following command::
|
||||
|
||||
easy_install guppy
|
||||
|
||||
.. _setuptools: http://pypi.python.org/pypi/setuptools
|
||||
pip install guppy
|
||||
|
||||
The telnet console also comes with a built-in shortcut (``hpy``) for accessing
|
||||
Guppy heap objects. Here's an example to view all Python objects available in
|
||||
|
|
@ -279,7 +280,8 @@ completely. To quote the paper:
|
|||
to move to a compacting garbage collector, which is able to move objects in
|
||||
memory. This would require significant changes to the Python interpreter.*
|
||||
|
||||
This problem will be fixed in future Scrapy releases, where we plan to adopt a
|
||||
new process model and run spiders in a pool of recyclable sub-processes.
|
||||
|
||||
.. _this paper: http://evanjones.ca/memoryallocator/
|
||||
|
||||
To keep memory consumption reasonable you can split the job into several
|
||||
smaller jobs or enable :ref:`persistent job queue <topics-jobs>`
|
||||
and stop/start spider from time to time.
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ the spider class as first argument in the :meth:`CrawlerRunner.crawl
|
|||
|
||||
.. seealso:: `Twisted Reactor Overview`_.
|
||||
|
||||
.. _run-multiple-spiders:
|
||||
|
||||
Running multiple spiders in the same process
|
||||
============================================
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue