淺度測評:requests、aiohttp、httpx 我應該用哪一個?

在武漢,房子裡待著,不出去影響世界了,轉載點文章。

在 Python 眾多的 HTTP 客戶端中,最有名的莫過於requests、aiohttp和httpx。在不借助其他第三方庫的情況下,requests只能傳送同步請求;aiohttp只能傳送非同步請求;httpx既能傳送同步請求,又能傳送非同步請求。

所謂的同步請求,是指在單程序單執行緒的程式碼中,發起一次請求後,在收到返回結果之前,不能發起下一次請求。所謂非同步請求,是指在單程序單執行緒的程式碼中,發起一次請求後,在等待網站返回結果的時間裡,可以繼續傳送更多請求。

今天我們來一個淺度測評,僅僅以多次傳送 POST 請求這個角度來對比這三個庫的效能。

測試使用的 HTTP 服務地址為http://122。51。39。219:8000/query,向它傳送 POST 請求的格式如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

請求傳送的 ts 欄位日期距離今天大於10天,那麼返回{“success”: false},如果小於等於10天,那麼返回{“success”: true}。

首先我們透過各個客戶端使用相同的引數只發送一次請求,看看效果。

傳送一次請求

requests

import requests

resp = requests。post(‘http://122。51。39。219:8000/query’,

json={‘ts’: ‘2020-01-20 13:14:15’})。json()

print(resp)

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

httpx

使用 httpx 傳送同步請求:

import httpx

resp = httpx。post(‘http://122。51。39。219:8000/query’,

json={‘ts’: ‘2020-01-20 13:14:15’})。json()

print(resp)

httpx 的同步模式與 requests 程式碼重合度99%,只需要把requests改成httpx即可正常執行。如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

使用 httpx 傳送非同步請求:

import httpx

import asyncio

asyncdefmain():

asyncwith httpx。AsyncClient() as client:

resp = await client。post(‘http://122。51。39。219:8000/query’,

json={‘ts’: ‘2020-01-20 13:14:15’})

result = resp。json()

print(result)

asyncio。run(main())

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

aiohttp

import aiohttp

import asyncio

asyncdefmain():

asyncwith aiohttp。ClientSession() as client:

resp = await client。post(‘http://122。51。39。219:8000/query’,

json={‘ts’: ‘2020-01-20 13:14:15’})

result = await resp。json()

print(result)

asyncio。run(main())

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

aiohttp 的程式碼與 httpx 非同步模式的程式碼重合度90%,只不過把AsyncClient換成了ClientSession,另外,在使用 httpx 時,當你await client。post時就已經發送了請求。但是當使用aiohttp時,只有在awiat resp。json() 時才會真正傳送請求。

傳送100次請求

我們現在隨機生成一個距離今天在5-15天的日期,傳送到 HTTP介面中。如果日期距離今天超過10天,那麼返回的資料的 False,如果小於等於10天,那麼返回的資料是 True。

我們傳送100次請求,計算總共耗時。

requests

在前幾天的文章中,我們提到,使用requests。post每次都會建立新的連線,速度較慢。而如果首先初始化一個 Session,那麼 requests 會保持連線,從而大大提高請求速度。所以在這次測評中,我們分別對兩種情況進行測試。

不保持連線

import random

import time

import datetime

import requests

defmake_request(body):

resp = requests。post(‘http://122。51。39。219:8000/query’, json=body)

result = resp。json()

print(result)

defmain():

start = time。time()

for _ in range(100):

now = datetime。datetime。now()

delta = random。randint(5, 15)

ts = (now - datetime。timedelta(days=delta))。strftime(‘%Y-%m-%d %H:%M:%S’)

make_request({‘ts’: ts})

end = time。time()

print(f‘傳送100次請求,耗時:{end - start}’)

if __name__ == ‘__main__’:

main()

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送100次請求,requests 不保持連線時耗時2.7秒

保持連線

對程式碼稍作修改,使用同一個 Session 傳送請求:

import random

import time

import datetime

import requests

defmake_request(session, body):

resp = session。post(‘http://122。51。39。219:8000/query’, json=body)

result = resp。json()

print(result)

defmain():

session = requests。Session()

start = time。time()

for _ in range(100):

now = datetime。datetime。now()

delta = random。randint(5, 15)

ts = (now - datetime。timedelta(days=delta))。strftime(‘%Y-%m-%d %H:%M:%S’)

make_request(session, {‘ts’: ts})

end = time。time()

print(f‘傳送100次請求,耗時:{end - start}’)

if __name__ == ‘__main__’:

main()

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送100次請求,requests 保持連線耗時1.4秒

httpx

同步模式

程式碼如下:

import random

import time

import datetime

import httpx

defmake_request(client, body):

resp = client。post(‘http://122。51。39。219:8000/query’, json=body)

result = resp。json()

print(result)

defmain():

session = httpx。Client()

start = time。time()

for _ in range(100):

now = datetime。datetime。now()

delta = random。randint(5, 15)

ts = (now - datetime。timedelta(days=delta))。strftime(‘%Y-%m-%d %H:%M:%S’)

make_request(session, {‘ts’: ts})

end = time。time()

print(f‘傳送100次請求,耗時:{end - start}’)

if __name__ == ‘__main__’:

main()

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送100次請求,httpx 同步模式耗時1.5秒左右。

非同步模式

程式碼如下:

import httpx

import random

import datetime

import asyncio

import time

asyncdefrequest(client, body):

resp = await client。post(‘http://122。51。39。219:8000/query’, json=body)

result = resp。json()

print(result)

asyncdefmain():

asyncwith httpx。AsyncClient() as client:

start = time。time()

task_list = []

for _ in range(100):

now = datetime。datetime。now()

delta = random。randint(5, 15)

ts = (now - datetime。timedelta(days=delta))。strftime(‘%Y-%m-%d %H:%M:%S’)

req = request(client, {‘ts’: ts})

task = asyncio。create_task(req)

task_list。append(task)

await asyncio。gather(*task_list)

end = time。time()

print(f‘傳送100次請求,耗時:{end - start}’)

asyncio。run(main())

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送100次請求,httpx 非同步模式耗時0.6秒左右。

aiohttp

測試程式碼如下:

import aiohttp

import random

import datetime

import asyncio

import time

asyncdefrequest(client, body):

resp = await client。post(‘http://122。51。39。219:8000/query’, json=body)

result = await resp。json()

print(result)

asyncdefmain():

asyncwith aiohttp。ClientSession() as client:

start = time。time()

task_list = []

for _ in range(100):

now = datetime。datetime。now()

delta = random。randint(5, 15)

ts = (now - datetime。timedelta(days=delta))。strftime(‘%Y-%m-%d %H:%M:%S’)

req = request(client, {‘ts’: ts})

task = asyncio。create_task(req)

task_list。append(task)

await asyncio。gather(*task_list)

end = time。time()

print(f‘傳送100次請求,耗時:{end - start}’)

asyncio。run(main())

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送100次請求,使用 aiohttp 耗時0.3秒左右

傳送1000次請求

由於 request 保持連線的速度比不保持連線快,所以我們這裡只用保持連線的方式來測試。並且不列印返回的結果。

requests

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送1000次請求,requests 耗時16秒左右

httpx

同步模式

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送1000次請求,httpx 同步模式耗時18秒左右

非同步模式

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送1000次請求,httpx 非同步模式耗時5秒左右

aiohttp

執行效果如下圖所示:

淺度測評:requests、aiohttp、httpx 我應該用哪一個?

傳送1000次請求,aiohttp 耗時4秒左右

總結

如果你只發幾條請求。那麼使用 requests 或者 httpx 的同步模式,程式碼最簡單。

如果你要傳送很多請求,但是有些地方要傳送同步請求,有些地方要傳送非同步請求,那麼使用 httpx 最省事。

如果你要傳送很多請求,並且越快越好,那麼使用 aiohttp 最快。

這篇測評文章只是一個非常淺度的評測,只考慮了請求速度這一個角度。如果你要在生產環境使用,那麼你可以做更多實驗來看是不是符合你的實際使用情況。