Какой является наилучший пример использования библиотеки aiohttp для создания веб-приложения?

Здравствуйте! Я разрабатываю веб-приложение с использованием <b>FastAPI</b> и для выполнения запросов применяю <b>aiohttp</b>. На данный момент, я создаю новую сессию для каждого запроса, используя контекстный менеджер, и закрываю её сразу после завершения запроса. Необходимо оценить, является ли такой подход оптимальным. Вот мой код: <br/> <pre><code class="python">from typing import Any

from aiohttp import ClientSession, ClientResponse
from aiohttp import ClientResponseError, ClientTimeout

from src.logger import get_logger
logger = get_logger(__name__)


class HTTPClient:
    def __init__(self, base_url: str = None, timeout: int = 60) -&gt; None:
        self.base_url = base_url
        self.timeout = ClientTimeout(timeout)

    async def _request(
        self,
        method: str,
        url: str,
        **kwargs: Any
    ) -&gt; ClientResponse:

        try:
            async with ClientSession(
                    self.base_url, timeout=self.timeout) as session:

                async with session.request(method, url, **kwargs) as response:
                    response.raise_for_status()

                    logger.info(f'[{response.method}]'
                                f'[{response.status}]'
                                f' -&gt; {response.url}')

                    return response if response else None

        except ClientResponseError as error:
            logger.error(f'CRE: [{error.request_info.method}][{error.status}]'
                         f' {error.request_info.url} -&gt; {error.message}')
            return None

        except Exception as exc:
            logger.error(f'Exception: {exc}')
            return None

    async def get(self, url: str, **kwargs: Any) -&gt; ClientResponse:
  • 7 ноября 2024 г. 0:53
Ответы на вопрос 2
Ваш текущий подход к использованию библиотеки `aiohttp` в контексте веб-приложения вполне рабочий, но создание нового экземпляра `ClientSession` для каждого запроса не является оптимальным. Это может привести к значительным накладным расходам, особенно если у вас много параллельных запросов, поскольку затраты на создание и закрытие сессии могут быть высокими.

### Рекомендация по улучшению

Рекомендуется создавать один экземпляр `ClientSession` и переиспользовать его в течение всего времени жизни вашего приложения. Ниже приведен переработанный пример, который создает `ClientSession` в методе `__init__` класса и закрывает его при завершении работы приложения.

```python
from typing import Any
from aiohttp import ClientSession, ClientResponse
from aiohttp import ClientResponseError, ClientTimeout
import logging

logger = logging.getLogger(__name__)

class HTTPClient:
    def __init__(self, base_url: str = None, timeout: int = 60) -> None:
        self.base_url = base_url
        self.timeout = ClientTimeout(timeout)
        self.session = ClientSession(base_url=self.base_url, timeout=self.timeout)

    async def _request(self, method: str, url: str, **kwargs: Any) -> ClientResponse:
        try:
            async with self.session.request(method, url, **kwargs) as response:
                response.raise_for_status()
                logger.info(f'[{response.method}][{response.status}] -> {response.url}')
                return response if response else None

        except ClientResponseError as error:
            logger.error(f'CRE: [{error.request_info.method}][{error.status}] {error.request_info.url} -> {error.message}')
            return None

        except Exception as exc:
            logger.error(f'Exception: {exc}')
            return None

    async def get(self, url: str, **kwargs: Any) -> ClientResponse:
        return await self._request('GET', url, **kwargs)

    async def close(self) -> None:
        await self.session.close()
```

### Дополнительные рекомендации

1. **Закрытие сессии**:
   Вызывайте метод `close()` после завершения работы вашего приложения или в какой-то момент, когда вы знаете, что больше не будете делать запросы. Если ваше приложение работает с FastAPI, вы можете использовать зависимость или событие старта/остановки приложения для управления жизненным циклом `ClientSession`.

2. **Исключения**:
   Обработку исключений можно улучшить с использованием более специфичных ошибок `aiohttp` и необходимости логирования.

3. **Перенос в FastAPI**:
   Вы можете создать зависимость в FastAPI для вашего клиента HTTP. Например:

   ```python
   from fastapi import FastAPI, Depends

   app = FastAPI()
   http_client = HTTPClient(base_url="https://api.example.com")

   @app.on_event("shutdown")
   async def shutdown_event():
       await http_client.close()

   async def get_http_client() -> HTTPClient:
       return http_client
   ```

   Затем вы можете использовать зависимость `get_http_client` в ваших эндпоинтах, чтобы получить доступ к `http_client`.

4. **Обработка ответов**:
   Подумайте о том, чтобы добавить методы для обработки ответов, чтобы не повторять код логирования и проверки состояния в каждом методе.

Эти изменения сделают ваше веб-приложение более производительным и простым в обслуживании.
пишу веб приложение с использованием FastAPI
В документации есть пример использования DI для прокидывания объекта сессии в рамках одного запроса (или любой другой реализации).

но вот пришло время остановить приложение и как только я его останавливаю - вероятнее всего у меня посыплются ошибки о не закрытом соединении
Вы можете выполнять любую логику при остановке, в FastApi для этого есть событие shutdown . Если у вас долгоживущие сессии, завершайте их в этом событии.
Похожие вопросы