在 Python 衆多的 HTTP 客戶端中,最有名的莫過於requests
、aiohttp
和httpx
。在不借助其餘第三方庫的狀況下,requests
只能發送同步請求;aiohttp
只能發送異步請求;httpx
既能發送同步請求,又能發送異步請求。python
所謂的同步請求,是指在單進程單線程的代碼中,發起一次請求後,在收到返回結果以前,不能發起下一次請求。所謂異步請求,是指在單進程單線程的代碼中,發起一次請求後,在等待網站返回結果的時間裏,能夠繼續發送更多請求。json
今天咱們來一個淺度測評,僅僅以屢次發送 POST 請求這個角度來對比這三個庫的性能。session
測試使用的 HTTP 服務地址爲http://122.51.39.219:8000/query,向它發送 POST 請求的格式以下圖所示:app
請求發送的 ts 字段日期距離今天大於10天,那麼返回{"success": false}
,若是小於等於10天,那麼返回{"success": true}
。dom
首先咱們經過各個客戶端使用相同的參數只發送一次請求,看看效果。異步
import requests
resp = requests.post('http://122.51.39.219:8000/query',
json={'ts': '2020-01-20 13:14:15'}).json()
print(resp)
複製代碼
運行效果以下圖所示:async
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
便可正常運行。以下圖所示:post
import httpx
import asyncio
async def main():
async with 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())
複製代碼
運行效果以下圖所示:性能
import aiohttp
import asyncio
async def main():
async with 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()
時纔會真正發送請求。
咱們如今隨機生成一個距離今天在5-15天的日期,發送到 HTTP接口中。若是日期距離今天超過10天,那麼返回的數據的 False,若是小於等於10天,那麼返回的數據是 True。
咱們發送100次請求,計算總共耗時。
在前幾天的文章中,咱們提到,使用requests.post
每次都會建立新的鏈接,速度較慢。而若是首先初始化一個 Session,那麼 requests 會保持鏈接,從而大大提升請求速度。因此在此次測評中,咱們分別對兩種狀況進行測試。
import random
import time
import datetime
import requests
def make_request(body):
resp = requests.post('http://122.51.39.219:8000/query', json=body)
result = resp.json()
print(result)
def main():
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
def make_request(session, body):
resp = session.post('http://122.51.39.219:8000/query', json=body)
result = resp.json()
print(result)
def main():
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秒
代碼以下:
import random
import time
import datetime
import httpx
def make_request(client, body):
resp = client.post('http://122.51.39.219:8000/query', json=body)
result = resp.json()
print(result)
def main():
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
async def request(client, body):
resp = await client.post('http://122.51.39.219:8000/query', json=body)
result = resp.json()
print(result)
async def main():
async with 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秒左右。
測試代碼以下:
import aiohttp
import random
import datetime
import asyncio
import time
async def request(client, body):
resp = await client.post('http://122.51.39.219:8000/query', json=body)
result = await resp.json()
print(result)
async def main():
async with 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秒左右
因爲 request 保持鏈接的速度比不保持鏈接快,因此咱們這裏只用保持鏈接的方式來測試。而且不打印返回的結果。
運行效果以下圖所示:
發送1000次請求,requests 耗時16秒左右
運行效果以下圖所示:
發送1000次請求,httpx 同步模式耗時18秒左右
運行效果以下圖所示:
發送1000次請求,httpx 異步模式耗時5秒左右
運行效果以下圖所示:
發送1000次請求,aiohttp 耗時4秒左右
若是你只發幾條請求。那麼使用 requests 或者 httpx 的同步模式,代碼最簡單。
若是你要發送不少請求,可是有些地方要發送同步請求,有些地方要發送異步請求,那麼使用 httpx 最省事。
若是你要發送不少請求,而且越快越好,那麼使用 aiohttp 最快。
這篇測評文章只是一個很是淺度的評測,只考慮了請求速度這一個角度。若是你要在生產環境使用,那麼你能夠作更多實驗來看是否是符合你的實際使用狀況。