那些坑儿系列--Python--2--爬虫 & asyncio 并发

没事干,写了个爬取视频的小程序。

由于视频被拆分的很小,单线程下载太慢,不高兴开线程,而最近又比较火asyncio,也花精力研究了一番,自觉够用了。

协程很实用,特别是io密集型,比线程方便的很多。

最开始还担心好多库不支持协程,实际上 loop.run_in_executor 直接搞定所有阻塞函数。

开始担心服务器有最大线程限制,所有写了个MAX_CONN_LIMIT限制协程的并发量,到达最大上限,就不再添加task。

结果程序写出来后测试一番,MAX_CONN_LIMIT 分别设置1,2,5,10,20 想试探一下服务器允许多少线程。
视频下载的也都挺顺利,但是速度还是一般,10左右就达到了15Mbps,20左右在17Mbps。
牙一咬,直接改50,速度还是在18Mbps不到。100?200?速度仍然是18M不到?
无论我的限制改多大,有多少个task再运行,下载速度总在18Mbps不到。

分析了一下可能情况:
1 服务器限速?(一般服务器限速都是线程限速,线程数量够多,不该如此。用迅雷直接将生成的几百url加进去,都能上100Mbps也就是12M/s。排除)
2 程序代码写太糟?(看了任务管理器,感觉cpu占用在50,100限制时也挺高啊)
3 requests对asyncio支持不够?(这次为了省事,没用urllib官方库,有可能)
4 python不支持那么多task?(这感觉不太可能,初学asyncio的时候写的爬网站信息的程序100多线程速度都是线性增长的)

用cprofile跑了遍程序,发现绝大多数时间在selector上,感觉问题也不大啊,协程task够多,selector开销是大啊!

最后,还是觉得可能在2个方面: requests和asyncio协程事件循环效率上。

我用的是asyncio.get_running_loop() 获取当前os线程中运行的事件循环
查了官方的doc:
class asyncio.SelectorEventLoop
class asyncio.ProactorEventLoop
并没有说哪个loop效率差,最多也就selectselector限制512socket。由于默认得到的loop是selectorEventLoop
考虑换种loop试一试。
随后打开了最初习作的爬网站的代码,想比较一下requests和urllib的差别(其实只用了requests的抓取功能,和urllib差不多)。
突然发现一个被遗忘的东西
awaitable loop.run_in_executor(executor, func, *args)
executor 用的时候,直接取了None,采用默认值。
翻了一下run_in_executorexecutor 为None 时,系统默认ThreadPoolExecutor 实例的 max_worker 为 cpu核心数*3。

恍然大悟。
重翻了一遍最新的python3.7 的文档,思路终于清晰起来。
loop的效率是足够的,不足的是executor。
python3.7,提供 loop.run_in_executor 支持非异步函数用于协程。
原理是开个线程池,或者进程池,将同步函数丢到线程池中运行,
而loop则在异步中切换,当线程池中的线程完成,再被切换回loop。
所以,谜底解开了,

无论tasks是多少个,线程池中运行的下载连接,只有 cpu核心数*3。

这就是为什么下载速度,再tasks数量升到20以后无法增长的原因。
解决方法很简单
定义一个线程更多的线程池
NOWTPOOL = ThreadPoolExecutor(max_workers=MAX_CONN_LIMIT)
实用该线程池代替默认的。

loop = asyncio.get_running_loop()
r = await loop.run_in_executor(NOWTPOOL,partial(requests.get,url,timeout=timeout))

直接将线程池的worker数量与tasks数量对应,改动后,150个tasks,速度上升到90Mbps,考虑到本身100M宽带,以及各种损耗,属于可接受范围。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值