Рубрики
Без рубрики

Начало работы с httpx, часть 3: Создание клиента отдыха Python (асинхронная версия)

Httpx – это современная библиотека клиента HTTP для Python. Его интерфейс похож на старый режим ожидания … Теги с Python, httpx, Asyncio, Pythonpoetry.

Httpx Это современная библиотека клиента HTTP для Python. Его интерфейс похож на старый режим ожидания Запросы , но он поддерживает асинхронные HTTP-запросы, используя Python’s асинсио Библиотека (или Trio ). Другими словами, в то время как ваша программа ждет http-запроса для завершения, другая работа не должна быть заблокирована.

В Часть 1 Мы построили простой инструмент поиска википедии с помощью Python и Httpx. . Хотя …| Httpx использовался, инструмент был только синхронным. Другими словами, каждый HTTP-запрос был передан последовательно, и последующие запросы только начинаются после завершения предыдущего. Много ожидания в очереди.

Теперь давайте сделаем то, что Httpx Хорошо для: асинхронные HTTP-запросы.

async и ждать

Питона асинсио Позволяет задачам сотрудничать. Когда задача занята ждать в ожидании ввода/вывода, он может предоставить другим задачам возможность выполнять свой бизнес.

Для обозначения такой функции предшествует этому с async ключевое слово. К Звоните Такая функция предшествовать призыву с Ждите ключевое слово.

Мы можем создать другой модуль Python (файл), SRC/Pypedia/Asynchronous.py со следующим кодом, который использует async и Ждите . Это почти так же, как код из Часть 1 с несколькими отличиями. Не стесняйтесь сравнивать два.

"""Proof-of-concept asynchronous Wikipedia search tool."""
import asyncio
import logging
import time

import httpx

EMAIL = "your_email@provider"  # or Github URL or other identifier
USER_AGENT = {"user-agent": f"pypedia/0.1.0 ({EMAIL})"}

logging.basicConfig(filename="asyncpedia.log", filemode="w", level=logging.INFO)
LOG = logging.getLogger("asyncio")


async def search(query, limit=100, client=None):
    """Search Wikipedia, returning a JSON list of pages."""
    if client:
        close_client = False
    else:
        client = httpx.AsyncClient()
        close_client = True
    LOG.info(f"Start query '{query}': {time.strftime('%X')}")
    url = "https://en.wikipedia.org/w/rest.php/v1/search/page"
    params = {"q": query, "limit": limit}
    response = await client.get(url, params=params)
    if close_client:
        await client.aclose()
    LOG.info(f"End query '{query}': {time.strftime('%X')}")
    return response


async def list_articles(queries):
    """Execute several Wikipedia searches."""
    async with httpx.AsyncClient(headers=USER_AGENT) as client:
        tasks = [search(query, client=client) for query in queries]
        responses = await asyncio.gather(*tasks)
    results = (response.json()["pages"] for response in responses)
    return dict(zip(queries, results))


def run():
    queries = [
        "linksto:Python_(programming_language)",
        "incategory:Computer_programming",
        "incategory:Programming_languages",
        "incategory:Python_(programming_language)",
        "incategory:Python_web_frameworks",
        "incategory:Python_implementations",
        "incategory:Programming_languages_created_in_1991",
        "incategory:Computer_programming_stubs",
    ]
    results = asyncio.run(list_articles(queries))
    for query, articles in results.items():
        print(f"\n*** {query} ***")
        for article in articles:
            print(f"{article['title']}: {article['excerpt']}")

Обратите внимание на использование httpx. Asyncclient а не httpx. Клиент , в обоих list_articles () и в Поиск () Отказ

В list_articles () Клиент используется в контексте. Потому что это асинхронный, контекстный менеджер использует async с не только с Отказ

В Поиск () , если клиент не указан, он создается, а не с менеджером контекста, а с клиент. Asyncclient () Отказ При использовании этого метода ответственность на нас, чтобы закрыть клиент с ждать клиента .aclose () Отказ Плохие новости, если мы забудем это сделать.

Наши две основные функции предшествовали async Ключевое слово, чтобы указать, что они асинхронизированы. Другими словами, они готовы делиться контроль над Структура событий когда пострадает своими большими пальцами.

Если бы было нужно позвонить Поиск () индивидуально, тогда мы могли бы сделать это с жду поиска () Отказ

Однако в этом случае нам нужно одновременно запустить несколько звонков на Поиск () Отказ

asyncio.ghather ()

list_articles () Функция вызывает ожидаемый Поиск () Функция с использованием Функция asyncio.ghather () Отказ Это создаст задачи для контура события и управляет их одновременно.

Удобно, asyncio.ghather () Возвращает список возвратных значений каждой задачи, в точное порядок были переданы функции.

Примечание: поставить Ждите до asyncio.ghather () , но не ставить Ждите до того, как функции переданы к нему. В ожидании каждого звонка будет обрабатываться asyncio.ghather () Отказ

Контур событий

Я уже упоминал Структура событий Пару раз. Я думаю о петле события как (должен быть только один) задача Runner для асинсио Приложения. Это обрабатывает задачи.

Расположение цикл события выполняется из единственной неожиданной функции в нашем скрипте. Я назвал функцию Беги () По совпадению, и это называет Функция высокого уровня asyncio.run () Отказ

Поместите другой путь, синхронная функция не может ждать асинхронная функция. Но это может asyncio.run () ] бегать Это.

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

Включить командный бегун

Наше Беги () Функция выполняет все, что мы хотим выполнить, когда вызывается как скрипт. В этом случае он создает список условий поиска, затем отправляет список на list_articles () Затем анализируют и печатают результат.

С поэзией точка входа для скрипта определяется в pyproject.toml. . Итак, мы добавляем это к этому файлу. Предполагая, что у вас уже было синхронно Синхридиция Определено, этот раздел теперь должен выглядеть так:

[tool.poetry.scripts]
asyncpedia = "pypedia.asynchronous:run"
syncpedia = "pypedia.synchronous:run"

Итак, скрипт Asyncedia позвонит Беги Функция асинхронный Подмодуль пакета Pypedia. . И, как уже определено, скрипт Синхридиция позвонит Беги Функция синхронизировать Подмодуль пакета Pypedia. .

Попробуйте:

poetry run asyncpedia

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

Преимущества производительности Async

В отличие от сценария от Часть 1 Вызывы в API Wikipedia сейчас случаются асинхронно , совместно поделив петлю событий. Один запрос, в ожидании ответа Wikipedia, может поделиться контролем контура событий с другими. Это можно увидеть в файле журнала.

$ cat asyncpedia.log
INFO:asyncio:Start query 'linksto:Python_(programming_language)': 06:03:39
INFO:asyncio:Start query 'incategory:Computer_programming': 06:03:39
INFO:asyncio:Start query 'incategory:Programming_languages': 06:03:39
INFO:asyncio:Start query 'incategory:Python_(programming_language)': 06:03:39
INFO:asyncio:Start query 'incategory:Python_web_frameworks': 06:03:39
INFO:asyncio:Start query 'incategory:Python_implementations': 06:03:39
INFO:asyncio:Start query 'incategory:Programming_languages_created_in_1991': 06:03:39
INFO:asyncio:Start query 'incategory:Computer_programming_stubs': 06:03:39
INFO:asyncio:End query 'incategory:Python_implementations': 06:03:39
INFO:asyncio:End query 'incategory:Python_(programming_language)': 06:03:39
INFO:asyncio:End query 'incategory:Programming_languages_created_in_1991': 06:03:39
INFO:asyncio:End query 'incategory:Python_web_frameworks': 06:03:39
INFO:asyncio:End query 'incategory:Computer_programming_stubs': 06:03:39
INFO:asyncio:End query 'incategory:Computer_programming': 06:03:40
INFO:asyncio:End query 'linksto:Python_(programming_language)': 06:03:40
INFO:asyncio:End query 'incategory:Programming_languages': 06:03:40

Обратите внимание, что времена начала/конца больше не являются последовательными (или, возможно, предсказуемыми). Они смешаны.

На моей машине синхронная версия завершается примерно через 7 секунд, в то время как эта асинхронная версия занимает около 2 секунд для завершения.

Это улучшение производительности!

Успех не успех, хотя, пока мы не будем повторять тесты, поскольку мы будем в следующей статье.

Оригинал: “https://dev.to/bowmanjd/getting-started-with-httpx-part-3-building-a-python-rest-client-asynchronous-version-3lb0”