在武漢,房子裡待著,不出去影響世界了,轉載點文章。
在 Python 眾多的 HTTP 客戶端中,最有名的莫過於requests、aiohttp和httpx。在不借助其他第三方庫的情況下,requests只能傳送同步請求;aiohttp只能傳送非同步請求;httpx既能傳送同步請求,又能傳送非同步請求。
所謂的同步請求,是指在單程序單執行緒的程式碼中,發起一次請求後,在收到返回結果之前,不能發起下一次請求。所謂非同步請求,是指在單程序單執行緒的程式碼中,發起一次請求後,在等待網站返回結果的時間裡,可以繼續傳送更多請求。
今天我們來一個淺度測評,僅僅以多次傳送 POST 請求這個角度來對比這三個庫的效能。
測試使用的 HTTP 服務地址為http://122。51。39。219:8000/query,向它傳送 POST 請求的格式如下圖所示:
請求傳送的 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)
執行效果如下圖所示:
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即可正常執行。如下圖所示:
使用 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())
執行效果如下圖所示:
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())
執行效果如下圖所示:
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()
執行效果如下圖所示:
傳送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()
執行效果如下圖所示:
傳送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()
執行效果如下圖所示:
傳送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())
執行效果如下圖所示:
傳送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())
執行效果如下圖所示:
傳送100次請求,使用 aiohttp 耗時0.3秒左右
傳送1000次請求
由於 request 保持連線的速度比不保持連線快,所以我們這裡只用保持連線的方式來測試。並且不列印返回的結果。
requests
執行效果如下圖所示:
傳送1000次請求,requests 耗時16秒左右
httpx
同步模式
執行效果如下圖所示:
傳送1000次請求,httpx 同步模式耗時18秒左右
非同步模式
執行效果如下圖所示:
傳送1000次請求,httpx 非同步模式耗時5秒左右
aiohttp
執行效果如下圖所示:
傳送1000次請求,aiohttp 耗時4秒左右
總結
如果你只發幾條請求。那麼使用 requests 或者 httpx 的同步模式,程式碼最簡單。
如果你要傳送很多請求,但是有些地方要傳送同步請求,有些地方要傳送非同步請求,那麼使用 httpx 最省事。
如果你要傳送很多請求,並且越快越好,那麼使用 aiohttp 最快。
這篇測評文章只是一個非常淺度的評測,只考慮了請求速度這一個角度。如果你要在生產環境使用,那麼你可以做更多實驗來看是不是符合你的實際使用情況。