Каким образом запросы асинхронны?

Я использую библиотеку запросов python в течение некоторого времени, и недавно мне нужно было сделать запрос асинхронно, то есть я хотел бы отправить HTTP-запрос, чтобы мой основной поток продолжал выполняться и вызывал обратный вызов, когда запрос возвращается.

Естественно, меня привели к библиотеке grequests (https://github.com/kennethreitz/grequests), но я запутался о поведении. Например:

import grequests

def print_res(res):
    from pprint import pprint
    pprint (vars(res))

req = grequests.get('http://www.codehenge.net/blog', hooks=dict(response=print_res))
res = grequests.map([req])

for i in range(10):
    print i

Приведенный выше код выдаст следующий вывод:

<...large HTTP response output...>

0
1
2
3
4
5
6
7
8
9

Очевидно, что вызов grequests.map() блокируется до тех пор, пока не будет доступен HTTP-ответ. Похоже, я неправильно понял «асинхронное» поведение здесь, и библиотека grequests предназначена только для одновременного выполнения нескольких HTTP-запросов и отправки всех ответов на один обратный вызов. Это точно?


person cacois    schedule 15.04.2013    source источник
comment
Не уверен, но нельзя ли просто использовать встроенный модуль urllib и запустить его в фоновом потоке с модулем thread?   -  person Aya    schedule 15.04.2013
comment
Я думаю, что мне, возможно, придется сделать именно это. Я просто был сбит с толку и хочу проверить ожидаемое поведение.   -  person cacois    schedule 15.04.2013
comment
Конечно. Я просто склоняюсь к встроенным, где это возможно, чтобы максимизировать портативность.   -  person Aya    schedule 15.04.2013


Ответы (3)


.map() предназначен для параллельного получения нескольких URL-адресов и действительно будет ждать завершения этих задач (вызывается gevent.joinall(jobs))).

Вместо этого используйте .send() для создания заданий с помощью Pool экземпляра. :

req = grequests.get('http://www.codehenge.net/blog', hooks=dict(response=print_res))
job = grequests.send(req, grequests.Pool(1))

for i in range(10):
    print i

Без пула вызов .send() все еще будет блокироваться, но только для вызова gevent.spawn(), который он выполняет.

person Martijn Pieters    schedule 15.04.2013
comment
@martijn pieters Можете ли вы дать образец с сообщением. Не удалось заставить его работать с почтовыми звонками - person Avinash; 24.09.2014
comment
@Avinash: вместо этого просто используйте grequests.post()? Что касается grequests, то между различными методами запроса нет никакой разницы. - person Martijn Pieters; 24.09.2014
comment
Что на самом деле делает .pool()? Я не понял документацию. - person user3347814; 05.09.2020

Если вы не хотите использовать grequests, вы можете просто реализовать запросы с обратными вызовами, используя requests + модуль threading из стандартной библиотеки. На самом деле это очень просто, и если все, что вы хотите делать, это отправлять запросы с обратными вызовами, API лучше, чем тот, который предоставляется grequests.

from threading import Thread

from requests import get, post, put, patch, delete, options, head



request_methods = {
    'get': get,
    'post': post,
    'put': put,
    'patch': patch,
    'delete': delete,
    'options': options,
    'head': head,
}


def async_request(method, *args, callback=None, timeout=15, **kwargs):
    """Makes request on a different thread, and optionally passes response to a
    `callback` function when request returns.
    """
    method = request_methods[method.lower()]
    if callback:
        def callback_with_args(response, *args, **kwargs):
            callback(response)
        kwargs['hooks'] = {'response': callback_with_args}
    kwargs['timeout'] = timeout
    thread = Thread(target=method, args=args, kwargs=kwargs)
    thread.start()

Вы можете убедиться, что это работает как вызовы AJAX в JS: вы отправляете запрос в другой поток, выполняете некоторые действия в основном потоке, а когда запрос возвращается, вы вызываете обратный вызов. Этот обратный вызов просто распечатывает содержимое ответа.

async_request('get', 'http://httpbin.org/anything', callback=lambda r: print(r.json()))
for i in range(10):
    print(i)
person kylebebak    schedule 05.07.2017

Создайте список запросов, а затем отправьте их с помощью .imap:

event_list = [grequests.get(url_viol_card, params={"viol": i},
              session=session) for i in print_ev_list]
for r in grequests.imap(event_list, size=5):
    print(r.request.url)
  • session — это requests.Session() объект (необязательно)
  • size=5 отправить 5 запросов одновременно: как только один из них будет выполнен, будет отправлен следующий
  • В этом примере, когда любой запрос (неупорядоченный) выполнен, его URL печатается
person Winand    schedule 15.02.2017