Python有了asyncio和aiohttp在爬蟲這類型IO任務中多線程/多進程還有存在的必要嗎?

最近正在學習Python中的異步編程,看了一些博客後作了一些小測驗:對比asyncio+aiohttp的爬蟲和asyncio+aiohttp+concurrent.futures(線程池/進程池)在效率中的差別,註釋:在爬蟲中我幾乎沒有使用任何計算性任務,爲了探測異步的性能,所有都只是作了網絡IO請求,就是說aiohttp把網頁get完就程序就done了。html

結果發現前者的效率比後者還要高。我詢問了另一位博主,(提供代碼的博主沒回我信息),他說使用concurrent.futures的話由於我所有都是IO任務,若是把這些IO任務分散到線程池/進程池,反而多線程/多進程之間的切換開銷還會下降爬蟲的效率。我想了想的確如此。node

那麼個人問題是:僅僅在爬取網頁的過程當中,就是request.get部分,多線程確定是沒有存在的必要了,由於GIL這個大坑,進程池可能好點,可是性能仍是不如異步爬蟲,並且更加浪費資源。既然這樣,是否是之後在爬蟲的爬取網頁階段咱們徹底均可以用興起的asyncio+aiohttp代替。(以及其餘IO任務好比數據庫/文件讀寫)git

固然在數據處理階段仍是要採用多進程,可是我以爲多線程是完全沒用了,本來它相比多進程的優點在於IO型任務,現看來在它的優點徹底被異步取代了。(固然問題創建在不考慮兼容2.x)github

注:還有一個額外的問題就是,看到一些博客說requests庫不支持異步編程是什麼意思,爲了充分發回異步的優點應該使用aiohttp,我沒有看過requests的源代碼,可是一些結果顯示aiohttp的性能確實更好,各位網友能解釋一下嗎?數據庫

代碼

asyncio+aiohttp編程

import aiohttp


async 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秒json

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倍segmentfault

import time
import requests
from concurrent.futures import ThreadPoolExecutor

NUMBERS = 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)。uvloopsanic以及500lines一個爬蟲項目已經開始這麼幹了。不討論兼容型問題,上面的見解是否正確,有一些什麼場景協程沒法取代多線程。網絡

異步有不少方案,twisted, tornado等都有本身的解決方案,問題創建在asyncio+aiohttp的協程異步。多線程

還有一個問題也想向各位網友請教一下


Python有了asyncio和aiohttp在爬蟲這類型IO任務中多線程/多進程還有存在的必要嗎? >> node.js

這個答案描述的挺清楚的:
http://www.goodpm.net/postreply/node.js/1010000007987098/Python有了asyncio和aiohttp在爬蟲這類型IO任務中多線程多進程還有存在的必要嗎.html
相關文章
相關標籤/搜索