Python Asyncio / Aiohttp обмена глобальными ресурсами по всему проекту - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть проект asyncio.Имеет несколько модулей.Многие из них нуждаются в доступе к нескольким глобальным переменным, таким как: 1. объект aiohttp ClientSession(), поскольку, согласно документам aiohttp, мне следует избегать создания новой ClientSession для каждого запроса.
2. сокет asyncio, то есть reader, writer, которыйЯ создаю, используя asyncio.open_connection().Я хочу поддерживать постоянное соединение.
3. Цикл событий, который я получаю с помощью asyncio.get_event_loop()

Как лучше всего делиться такими переменными?
Я хотел бы создать globals.py модуль, который будет определять эти переменные.

Проблема в том, что я не могу использовать синтаксис async with для объекта ClientSession в модуле globals.
Для сокета я должен как-то определить его в async def, поэтому яне может выставить его на уровне модуля.

И, если проверять, - должен ли каждый модуль определять глобальную переменную, такую ​​как:
loop = asyncio.get_event_loop()
Или лучше передать цикл событий вмодуль, например в классе __init__)?

1 Ответ

0 голосов
/ 19 декабря 2018

Нет необходимости использовать глобалы.Вместо этого создайте класс, в котором хранятся «глобальные» данные, и сделайте ваши функции методами этого класса.Например:

class Operation:
    def __init__(self):
        self._http = aiohttp.ClientSession()

    async def open(self):
        self._reader, self._writer = \
            await asyncio.open_connection(<host>, <port>)

    # ... more methods ...

    async def close(self):
        await self._http.close()
        self._writer.close()
        await self._writer.wait_closed()

Это имеет несколько преимуществ:

  • Это не требует какого-либо глобального состояния;
  • У вас есть полный контроль над тем, где создается состояниеи уничтожен (все сразу);
  • Если вы решите, что вам нужно выполнить несколько глобальных операций параллельно, это легко сделать - просто создайте несколько экземпляров Operation.
  • При тестировании вы можете просто создавать и разбирать Operation экземпляров по мере необходимости.

С этим ваша сопрограмма main может выглядеть следующим образом:

async def main():
    op = Operation()
    await op.open()
    try:
        await op.do_something()
        ...
    finally:
        await op.close()

asyncio.run(main())
#or asyncio.get_event_loop().run_until_complete(main())

Обратите внимание, что цикл обработки событий не хранится в объекте или передается ему каким-либо образом.Это связано с тем, что цикл обработки событий всегда можно получить с помощью asyncio.get_event_loop(), который при вызове из сопрограммы гарантированно возвращает цикл, который в данный момент выполняется.

...