Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程/多进程,有存在的必要吗?,asyncioaiohttp,最近正在学习Python
Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程/多进程,有存在的必要吗?,asyncioaiohttp,最近正在学习Python
最近正在学习Python中的异步编程,看了一些博客后做了一些小测验:对比asyncio+aiohttp的爬虫和asyncio+aiohttp+concurrent.futures(线程池/进程池)在效率中的差异,注释:在爬虫中我几乎没有使用任何计算性任务,为了探测异步的性能,全部都只是做了网络IO请求,就是说aiohttp把网页get完就程序就done了。
结果发现前者的效率比后者还要高。我询问了另外一位博主,(提供代码的博主没回我信息),他说使用concurrent.futures的话因为我全部都是IO任务,如果把这些IO任务分散到线程池/进程池,反而多线程/多进程之间的切换开销还会降低爬虫的效率。我想了想的确如此。
那么我的问题是:仅仅在爬取网页的过程中,就是request.get部分,多线程肯定是没有存在的必要了,因为GIL这个大坑,进程池可能好点,但是性能还是不如异步爬虫,而且更加浪费资源。既然这样,是不是以后在爬虫的爬取网页阶段我们完全都可以用兴起的asyncio+aiohttp代替。(以及其他IO任务比如数据库/文件读写)
当然在数据处理阶段还是要采用多进程,但是我觉得多线程是彻底没用了,原本它相比多进程的优势在于IO型任务,现看来在它的优势完全被异步取代了。(当然问题建立在不考虑兼容2.x)
注:还有一个额外的问题就是,看到一些博客说requests库不支持异步编程是什么意思,为了充分发回异步的优势应该使用aiohttp,我没有看过requests的源代码,但是一些结果显示aiohttp的性能确实更好,各位网友能解释一下吗?
代码
asyncio+aiohttp
import aiohttpasync def fetch_async(a): async with aiohttp.request('GET', URL.format(a)) as r: data = await r.json() return data['args']['a'] start = time.time()event_loop = asyncio.get_event_loop()tasks = [fetch_async(num) for num in NUMBERS]results = event_loop.run_until_complete(asyncio.gather(*tasks))for num, result in zip(NUMBERS, results): print('fetch({}) = {}'.format(num, result))
asyncio+aiohttp+线程池比上面要慢1秒
async def fetch_async(a): async with aiohttp.request('GET', URL.format(a)) as r: data = await r.json() return a, data['args']['a']def sub_loop(numbers): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) tasks = [fetch_async(num) for num in numbers] results = loop.run_until_complete(asyncio.gather(*tasks)) for num, result in results: print('fetch({}) = {}'.format(num, result))async def run(executor, numbers): await asyncio.get_event_loop().run_in_executor(executor, sub_loop, numbers)def chunks(l, size): n = math.ceil(len(l) / size) for i in range(0, len(l), n): yield l[i:i + n] event_loop = asyncio.get_event_loop()tasks = [run(executor, chunked) for chunked in chunks(NUMBERS, 3)]results = event_loop.run_until_complete(asyncio.gather(*tasks))print('Use asyncio+aiohttp+ThreadPoolExecutor cost: {}'.format(time.time() - start))
传统的requests + ThreadPoolExecutor比上面慢了3倍
import timeimport requestsfrom concurrent.futures import ThreadPoolExecutorNUMBERS = range(12)URL = 'http://httpbin.org/get?a={}'def fetch(a): r = requests.get(URL.format(a)) return r.json()['args']['a']start = time.time()with ThreadPoolExecutor(max_workers=3) as executor: for num, result in zip(NUMBERS, executor.map(fetch, NUMBERS)): print('fetch({}) = {}'.format(num, result))print('Use requests+ThreadPoolExecutor cost: {}'.format(time.time() - start))
补充
以上问题建立在CPython,至于我喜欢用多线程,不喜欢协程风格这类型的回答显然不属于本题讨论范畴。我主要想请教的是:
如果Python拿不下GIL,我认为未来理想的模型应该是多进程 + 协程(asyncio+aiohttp)。uvloop和sanic以及500lines一个爬虫项目已经开始这么干了。不讨论兼容型问题,上面的看法是否正确,有一些什么场景协程无法取代多线程。
异步有很多方案,twisted, tornado等都有自己的解决方案,问题建立在asyncio+aiohttp的协程异步。
还有一个问题也想向各位网友请教一下
Python多线程由于GIL的存在并不实用,但多进程还是很有用的
看看这篇文章: http://aosabook.org/en/500L/a...
asyncio 需要异步的 API 来配套(同步非阻塞 API 也可以,但是 Python 没有setInterval这种东西,可能需要 Hack 一下)。
如果是同步阻塞的 API,一个回调卡了其它回调都不能执行。你可以看一看,你到目前见到的 IO API 基本都是阻塞的。
asyncio采用的是协程的思想,就是在一个线程中处理多个异步任务。异步任务有那些呢,比如定时,异步IO等等。
但是如果任务不支持异步呢?
比如读写一个阻塞IO,或者进项耗时的大量的计算。协程就会任务阻塞问题,多进程多线程的优点就体现出来了。
两者的使用场景不一样。不同场景,不同方案。
对Python爬虫的了解的不多,但是一般做爬虫不是用Scrapy的吗.那个本身就是基于twisted异步框架的.
多进程可以充分利用多核,目前来说理想的是多进程+协程.
因为requests中还是使用同步的方法,所以会阻塞线程,这样的话用异步也没有意义了.你可以理解成在asyncio中使用time.sleep方法而不是asyncio.sleep方法.
asyncio需要相关的第三方的库支持,所以,基本上原来有的第三方库都需要单独写,如串口,网络协议,包括requests和http这些,不好,好的情况下,经过这两个版本的时间,很多用到的库都已经有了异步的了。包括requests.
编橙之家文章,
相关内容
- 如何将flask安装扩展在ext包里面,flask扩展ext包,如图:
- Python哪个模块适合处理文中这种格式文件,,如题文件格
- PEP 8标准里不推荐len()判断list是否为空的原因,peplen,假
- Ubuntu16.04 linux中文输入法不能正常跟随怎么解决?,u
- 动态Python代码注入怎么做才能预防安全性呢?,python安全
- Python编码关于gb2312、utf、ansi、gbk、Unicode等汇总问题,
- python mysql数据库做insert操作时报_mysql_exceptions.Programmi
- python能把带等号的字符串转换成字典类型吗,python等号
- python jinja2模板语言获取javascript function(_index) 传过来的
- Python web开发给controller、action附加属性如何操作,pyth
评论关闭