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

在 Python 衆多的 HTTP 客戶端中,最有名的莫過於requestsaiohttphttpx。在不借助其餘第三方庫的狀況下,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

首先咱們經過各個客戶端使用相同的參數只發送一次請求,看看效果。異步

發送一次請求

requests

import requests

resp = requests.post('http://122.51.39.219:8000/query',
                     json={'ts': '2020-01-20 13:14:15'}).json()
print(resp)
複製代碼

運行效果以下圖所示:async

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便可正常運行。以下圖所示:post

使用 httpx 發送異步請求:

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())
複製代碼

運行效果以下圖所示:性能

aiohttp

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() 時纔會真正發送請求。

發送100次請求

咱們如今隨機生成一個距離今天在5-15天的日期,發送到 HTTP接口中。若是日期距離今天超過10天,那麼返回的數據的 False,若是小於等於10天,那麼返回的數據是 True。

咱們發送100次請求,計算總共耗時。

requests

在前幾天的文章中,咱們提到,使用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秒

httpx

同步模式

代碼以下:

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秒左右。

aiohttp

測試代碼以下:

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秒左右

發送1000次請求

因爲 request 保持鏈接的速度比不保持鏈接快,因此咱們這裏只用保持鏈接的方式來測試。而且不打印返回的結果。

requests

運行效果以下圖所示:

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

httpx

同步模式

運行效果以下圖所示:

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

異步模式

運行效果以下圖所示:

發送1000次請求,httpx 異步模式耗時5秒左右

aiohttp

運行效果以下圖所示:

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

總結

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

若是你要發送不少請求,可是有些地方要發送同步請求,有些地方要發送異步請求,那麼使用 httpx 最省事。

若是你要發送不少請求,而且越快越好,那麼使用 aiohttp 最快。

這篇測評文章只是一個很是淺度的評測,只考慮了請求速度這一個角度。若是你要在生產環境使用,那麼你能夠作更多實驗來看是否是符合你的實際使用狀況。

相關文章
相關標籤/搜索