Поверхностная оценка: запросы, aiohttp, httpx, что мне следует использовать?

Python HTTP

Среди многих HTTP-клиентов Python самым известным является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 на 99% идентичен коду запросов, нужно только поставитьrequestsизменить наhttpxнормально работать. Как показано ниже:

Отправьте асинхронный запрос с помощью 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 на 90% идентичен коду асинхронного режима httpx, но кодAsyncClientзаменяетсяClientSession, а также при использовании httpx, когда выawait client.postзапрос отправлен. но при использованииaiohttp, только тогда, когдаawiat resp.json()Только когда запрос действительно отправлен.

отправить 100 запросов

Теперь мы случайным образом генерируем дату, отстоящую от сегодняшнего дня на 5-15 дней, и отправляем ее в HTTP-интерфейс. Если дата отличается от сегодняшнего дня более чем на 10 дней, возвращаемые данные имеют значение False, если она меньше или равна 10 дням, возвращаемые данные имеют значение True.

Мы отправляем 100 запросов, и расчет занимает в общей сложности время.

requests

Несколько дней назад в статье мы упомянули, что использованиеrequests.postКаждый раз создается новое соединение, которое работает медленнее. И если сеанс инициализируется первым, запросы сохранят соединение, что значительно повышает скорость запросов. Таким образом, в этой оценке мы тестировали два случая отдельно.

не оставаться на связи

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 запросов занимает 2,7 секунды, когда запросы не поддерживают связь

оставайся на связи

Небольшая модификация кода для отправки запроса с использованием той же сессии:

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 запросов требуется 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 запросов занимает около 0,3 секунды с использованием aiohttp

Отправить 1000 запросов

Поскольку скорость запроса, сохраняющего соединение, выше, чем скорость отсутствия соединения, мы используем только метод сохранения соединения для тестирования здесь. и не печатает возвращенный результат.

requests

Эффект операции показан на следующем рисунке:

Отправьте 1000 запросов, запросы занимают около 16 секунд

httpx

Режим синхронизации

Эффект операции показан на следующем рисунке:

Отправьте 1000 запросов, синхронный режим httpx занимает около 18 секунд

Асинхронный режим

Эффект операции показан на следующем рисунке:

Отправьте 1000 запросов, асинхронный режим httpx занимает около 5 секунд

aiohttp

Эффект операции показан на следующем рисунке:

Отправка 1000 запросов, aiohttp занимает около 4 секунд

Суммировать

Если вы отправляете только несколько запросов. Тогда используйте синхронный режим запросов или httpx, код самый простой.

Если вы отправляете много запросов, но некоторые места отправляют синхронные запросы, а некоторые места отправляют асинхронные запросы, использование httpx — самый простой способ.

Если вы отправляете много запросов и чем быстрее, тем лучше, то использование aiohttp будет самым быстрым.

Эта обзорная статья является лишь очень поверхностным обзором, учитывающим только скорость запросов. Если вы хотите использовать его в производственной среде, вы можете провести больше экспериментов, чтобы увидеть, соответствует ли он вашему фактическому варианту использования.