scrapy 2.3 使用調(diào)試內(nèi)存泄漏 trackref

2021-06-17 11:56 更新

?trackref? 是Scrapy提供的一個(gè)模塊,用于調(diào)試最常見(jiàn)的內(nèi)存泄漏情況。它基本上跟蹤對(duì)所有實(shí)時(shí)請(qǐng)求、響應(yīng)、項(xiàng)、蜘蛛和選擇器對(duì)象的引用。

您可以進(jìn)入telnet控制臺(tái)并使用 ?prefs()? 函數(shù)的別名 ?print_live_refs()? 功能:

telnet localhost 6023

>>> prefs()
Live References

ExampleSpider                       1   oldest: 15s ago
HtmlResponse                       10   oldest: 1s ago
Selector                            2   oldest: 0s ago
FormRequest                       878   oldest: 7s ago

如您所見(jiàn),該報(bào)告還顯示了每個(gè)類(lèi)中最舊對(duì)象的“年齡”。如果每個(gè)進(jìn)程運(yùn)行多個(gè)spider,那么通過(guò)查看最早的請(qǐng)求或響應(yīng),您很可能會(huì)發(fā)現(xiàn)哪個(gè)spider正在泄漏。您可以使用 ?get_oldest()? 功能(從telnet控制臺(tái))。

跟蹤哪些對(duì)象?

被跟蹤的對(duì)象 ?trackrefs? 都來(lái)自這些類(lèi)(及其所有子類(lèi)):

  • ?scrapy.http.Request?
  • ?scrapy.http.Response?
  • ?scrapy.item.Item?
  • ?scrapy.selector.Selector?
  • ?scrapy.spiders.Spider?

一個(gè)真實(shí)的例子

讓我們來(lái)看一個(gè)假設(shè)的內(nèi)存泄漏案例的具體示例。假設(shè)我們有一只蜘蛛,上面有一條和這條類(lèi)似的線:

return Request(f"http://www.somenastyspider.com/product.php?pid={product_id}",
               callback=self.parse, cb_kwargs={'referer': response})

該行正在請(qǐng)求中傳遞一個(gè)響應(yīng)引用,它有效地將響應(yīng)生命周期與請(qǐng)求的生命周期聯(lián)系起來(lái),這肯定會(huì)導(dǎo)致內(nèi)存泄漏。

讓我們看看如何通過(guò)使用 ?trackref? 工具。

當(dāng)爬蟲(chóng)運(yùn)行幾分鐘后,我們注意到它的內(nèi)存使用量增長(zhǎng)了很多,我們可以進(jìn)入它的telnet控制臺(tái)并檢查實(shí)時(shí)引用:

>>> prefs()
Live References

SomenastySpider                     1   oldest: 15s ago
HtmlResponse                     3890   oldest: 265s ago
Selector                            2   oldest: 0s ago
Request                          3878   oldest: 250s ago

事實(shí)上,存在如此多的實(shí)時(shí)響應(yīng)(而且它們太老了),這是絕對(duì)可疑的,因?yàn)榕c請(qǐng)求相比,響應(yīng)的生存期應(yīng)該相對(duì)較短。響應(yīng)的數(shù)量與請(qǐng)求的數(shù)量相似,因此看起來(lái)它們是以某種方式捆綁在一起的。我們現(xiàn)在可以檢查蜘蛛的代碼,以發(fā)現(xiàn)產(chǎn)生泄漏的討厭的行(在請(qǐng)求中傳遞響應(yīng)引用)。

有時(shí),有關(guān)活動(dòng)對(duì)象的額外信息可能會(huì)有所幫助。讓我們檢查最早的回答:

>>> from scrapy.utils.trackref import get_oldest
>>> r = get_oldest('HtmlResponse')
>>> r.url
'http://www.somenastyspider.com/product.php?pid=123'

如果您希望遍歷所有對(duì)象,而不是獲取最舊的對(duì)象,則可以使用 ?scrapy.utils.trackref.iter_all()? 功能:

>>> from scrapy.utils.trackref import iter_all
>>> [r.url for r in iter_all('HtmlResponse')]
['http://www.somenastyspider.com/product.php?pid=123',
 'http://www.somenastyspider.com/product.php?pid=584',
...]

蜘蛛太多了?

如果項(xiàng)目并行執(zhí)行的spider太多,則 ?prefs()? 很難閱讀。因此,該函數(shù)具有 ?ignore? 參數(shù),該參數(shù)可用于忽略特定類(lèi)(及其所有子類(lèi))。例如,這不會(huì)顯示對(duì)spider的任何實(shí)時(shí)引用:

>>> from scrapy.spiders import Spider
>>> prefs(ignore=Spider)

scrapy.utils.trackRef模塊

以下是 ?trackref? 模塊。

classscrapy.utils.trackref.object_ref

如果要使用跟蹤活動(dòng)實(shí)例,則從該類(lèi)繼承 trackref 模塊。

scrapy.utils.trackref.print_live_refs(class_nameignore=NoneType)

打印實(shí)時(shí)引用的報(bào)告,按類(lèi)名分組。

參數(shù)

ignore (type or tuple) -- 如果給定,則將忽略指定類(lèi)(或類(lèi)的元組)中的所有對(duì)象。

scrapy.utils.trackref.get_oldest(class_name)

返回具有給定類(lèi)名的最舊活動(dòng)對(duì)象,或者 None 如果沒(méi)有找到。使用 ?print_live_refs()? 首先獲取每個(gè)類(lèi)名的所有跟蹤活動(dòng)對(duì)象的列表。

scrapy.utils.trackref.iter_all(class_name)

返回具有給定類(lèi)名的所有活動(dòng)對(duì)象的迭代器,或者 None 如果沒(méi)有找到。使用 ?print_live_refs()? 首先獲取每個(gè)類(lèi)名的所有跟蹤活動(dòng)對(duì)象的列表。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)