requests模块bug之内存泄漏问题
大胆猜想,步步验证,就可以初步定位内存溢出位置。内存溢出的异常和其他爬虫中遇到的异常最大的区别就是无法被捕捉,你可以捕捉服务器返回数据异常、可以捕捉获取代理异常、可以捕捉验证码未通过异常、可以捕捉请求异常,但是你无法捕捉内存溢出异常。内存泄漏导致的报错不容易看出来,开发环境中一般没有人太关注内存,所以情况就是请求数据量大的前提下,项目在本地开发环境运行会报错其他错误导致项目停止运行。生产环境中,如
背景
工作中遇到了一次requests模块导致的内存泄漏问题,内存泄漏不太好检查,记录一下整个问题状态、检查过程以及解决方案。
开发环境状态描述
内存泄漏导致的报错不容易看出来,开发环境中一般没有人太关注内存,所以情况就是请求数据量大的前提下,项目在本地开发环境运行会报错其他错误导致项目停止运行。
报错信息可能是比如说某些接口请求失败:由于系统缓存区空间不足或队列已满,不能执行套接字上的操作。
该状态报错信息是内存已满导致的程序报错,比较少见,除非你开了电脑管家时刻看看内存,不然不容易发现。
生产环境状态描述
生产环境中,如果是部署k8s上,数据量小项目中,该requests内存溢出问题,更不容易被发现,因为k8s上会自动扩容。
如果是一次性任务,他的运行结果是跑着跑着服务器给你跑崩了,产生痛苦和无力感,因为没有报错信息。如果不是想到可能是内存溢出,检查内存,不可能会发现该异常。
异常捕捉问题
内存溢出的异常和其他爬虫中遇到的异常最大的区别就是无法被捕捉,你可以捕捉服务器返回数据异常、可以捕捉获取代理异常、可以捕捉验证码未通过异常、可以捕捉请求异常,但是你无法捕捉内存溢出异常。
如果按照报错信息去捕捉异常,尝试重新跑也是行不通的,内存溢出会导致程序直接崩溃。不会再运行。
排查和解决思路
生存环境对内存进行检查即可发现内存溢出异常,未解决项目内存溢出bug前,数据量不大的一次性任务可以通过杀进程让程序重新跑数据。
内存溢出来自requests模块,项目代码debugger,作用域内是无法进行定位的。只能项目代码中不断修改代码,观察内存变化。大胆猜想,步步验证,就可以初步定位内存溢出位置。当排查到内存溢出问题是requests封装后的base_requests导致之后,进一步排查异常就较为容易了。
在请求前后加入代码 objgraph.show_growth() 即可观察到requests请求执行后内存异常的增加了,按理说内存应该不变的。该代码运行本该不会有任何数据增加,不会print任何数据,实际却打印了许多奇怪的数据。
此时已经定位到具体哪行了,直接搜即可。
搜这个requests.session Memory leak 看到requests的issue里面有讨论,看了一下,应该就是这个问题了。
该问题为requests的session在使用代理的时候会有内存泄漏问题。
后续修复:首选方案是项目不使用session对象的话,直接去掉session,否则可以考虑如下两种方案尝试修复:
1.定期清理session:使用session.clear()方法清除session中的cookies和其他数据,或者使用del语句删除不再需要的响应和请求对象。
2.使用contextlib.closing:使用该函数确保文件对象在离开with块时关闭。
参考资料
Python内存排查 :https://segmentfault.com/a/1190000038277797
requests.session Memory leak :https://github.com/psf/requests/issues/4601
更多推荐
所有评论(0)