asyncio + aiohttp: почему мой клиент все еще блокирует?

Я пытаюсь протестировать свой http-клиент или приложение python 3.4.3, которое отправляет HTTP-запросы на сервер. Если по какой-либо причине у сервера есть задержка для ответа, я ожидаю, что запросы не должны блокироваться, поскольку asyncio + aiohttp должен обеспечивать асинхронные вызовы:

def post(self):
    print("received post")
    print(self.request)
    print("POST Body: %s" % str(self.request.body))
    time.sleep(3)
    self.write("blah")
    self.finish()

Мне просто интересно, почему мой код / ​​http-клиент блокирует:

import aiohttp, asyncio, async_timeout

@asyncio.coroutine
def fetch(session, url):
    with aiohttp.Timeout(30):
        try:
            response = yield from session.get(url)
            print((yield from response.read()))
            return response
        except Exception as e:
            raise e
        finally:
            try:
                response.release()
            except:
                pass

@asyncio.coroutine
def post(session, url):
    with aiohttp.Timeout(30):
        try:
            response = yield from session.post(url)
            print((yield from response.read()))
            return response
        except Exception as e:
            raise e
        finally:
            try:
                response.release()
            except:
                pass

@asyncio.coroutine
def close_connection(session):
    try:
        session.close()
    except:
        pass

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    session = aiohttp.ClientSession(loop=loop)
    try:
        for i in range(10):
            html = loop.run_until_complete(post(session, 'http://localhost:8000'))
    except Exception as e:
        print("received exception %s." % type(e).__name__)

        # Close session if not closed.
    loop.run_until_complete(close_connection(session))

Я попытался сделать цикл, в котором я перебираю 10 URL-адресов (здесь он тот же). Если бы это было последовательно, я бы ожидал, что это займет ~ 30 секунд (сервер отвечает с 3-секундной задержкой). Я ожидаю, что с асинхронным режимом потребуется меньше времени.

try:
    for i in range(10):
        html = loop.run_until_complete(post(session, 'http://localhost:8000'))

Блокирует ли это функция run_until_complete ()? Как сделать его неблокирующим?


person ozn    schedule 30.01.2018    source источник
comment
Лучше, если вы сможете уточнить ожидаемое поведение. Вы имеете в виду, что loop.run_until_complete(close_connection(session)) не выполняется одновременно с loop.run_until_complete(post(session, 'http://localhost:8000'))?   -  person caramel1995    schedule 30.01.2018
comment
Обновлено описание.   -  person ozn    schedule 30.01.2018


Ответы (1)


По сути, когда вы используете run_until_complete(), вы говорите циклу событий запустить сопрограммы, переданные в качестве аргумента, и вернуть результат по завершении. Короче говоря, run_until_complete() будет блокироваться, пока не завершит эту операцию.

На основе фрагмента кода цикла for. В основном в каждом цикле run_until_complete будет блокироваться, запускать сопрограмму (post в этом контексте), возвращать результат, после чего выполнение будет продолжено только для следующего цикла.

Здесь вы хотели запустить все post одновременно. Что вы можете сделать, так это использовать asyncio.gather().

try:
    html = loop.run_until_complete(asyncio.gather(*[post(session, 'http://localhost:8000') for i in range(10)]))
person caramel1995    schedule 30.01.2018
comment
Это ошибка, которую я получил: Traceback (most recent call last): File "test.py", line 48, in <module> html = loop.run_until_complete(asyncio.gather([post(session, 'http://localhost:8000')])) File "/usr/lib/python3.4/asyncio/tasks.py", line 565, in gather for arg in set(coros_or_futures): TypeError: unhashable type: 'list' - person ozn; 30.01.2018
comment
Фиксированный. Забыл распаковать список - person caramel1995; 30.01.2018
comment
Спасибо! Я хочу понять, это через передачу списка. Есть ли способ заставить aiohttp "запустить и забыть"? - person ozn; 30.01.2018
comment
@ozn Можете ли вы подробнее описать, что вы подразумеваете под огнем и забыли здесь? - person user4815162342; 30.01.2018
comment
Возможно, @ozn предпочитает ненадежные программы :) - person Andrew Svetlov; 31.01.2018
comment
Моя ошибка, думаю, мне нужно еще подправить свое описание; Пост, который я сделал, был только меньшего размера, чем я планировал. По сути, то, что у меня есть в main или (main ()), является частью другого метода (давайте назовем его make_a_post ()) в объекте (классе). Когда я вызываю экземпляр класса ‹› .make_a_post () 10 раз подряд, он завершается примерно через 30 секунд (задержка в 3 секунды на сервере * 10 транзакций). I компоненты, вызывающие ‹объектный экземпляр› .make_a_post (), также должны обрабатываться асинхронно. После исследования, да, выстрелить и забыть немного бессмысленно. - person ozn; 31.01.2018
comment
@ozn make_a_post - это сопрограмма? asyncio.gather, как показано в этом ответе, выполняет предоставленные сопрограммы параллельно, поэтому asyncio.gather([self.make_a_post(...) for x in bla]) должен делать то, что вы хотите. - person user4815162342; 31.01.2018