欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用py-spy解决scrapy卡死的问题

程序员文章站 2024-02-25 15:08:03
...

背景

在使用scrapy爬取东西的时候,使用crontab定时的启动爬虫,但是发现机器上经常产生很多卡死的scrapy进程,一段时间不管的话,会导致有10几个进程都卡死在那,并且会导致数据产出延迟。

问题定位

使用py-spy这个非常好用的python性能分析工具来进行排查,py-spy可以查看一个python进程函数调用用时,类似unix下的top命令。所以我们用这个工具看看是什么函数一直在执行。

首先安装这个工具

pip install py-spy

用py-spy看看scrapy哪个函数执行时间长

# 先找到这个卡死的scrapy进程的pid
ps -ef |grep scrapy 
# 启动 py-spy 观察这进程
py-spy top --pid 53424

首先我们按3,按OwnTime进行排序,这个表示函数自身执行的时间,可以看到read这个函数执行的时间最长,那看来是IO导致的,程序中的IO行为就是读写磁盘和网络IO,磁盘读写一般不会有问题,所以初步定位是网络IO导致的。
使用py-spy解决scrapy卡死的问题
接下来进行进一步确认,再按4,按TotalTIme 所有子函数执行时间总和进行排序,可以看到是在process_item和download,upload_image这些主流程函数的执行时间比较长,这一步是先把图片下载到本地,然后上传到静床,看来是下载这步从网络中read数据时出现了问题,进一步追踪代码。
使用py-spy解决scrapy卡死的问题
看下download的函数的代码:

if filename == '':
            filename = os.path.basename(url)
        path = path + '/' + filename
        
        try:
            res = request.urlretrieve(url,filename=path)
            print(url,res)
            return path
        except Exception as e:
            print('download img failed')
            print(e)
            return False

可以看到用了urllib这个库里面request.urlretrieve函数,这个函数是用来下载文件的,去看看python官网文档的函数说明,发现里面没有超时时间这个参数,所以是由于没有超时时间,导致一直在read,进而使得进程卡死。

urllib.request.urlretrieve(url, filename=None, reporthook=None,
data=None)

解决方案

使用另一种方式来下载图片,使用支持超时时间的urlopen函数,封装成一个自定义的url_retrieve,这样就不再会出现没有超时导致的卡死问题了。

def url_retrieve(self,url, path):
        r = request.urlopen(url, timeout=5)
        res = False
        with open(path,"wb") as f:
            res = f.write(r.read())
            f.flush()
            f.close()
        return res