Каким образом grequests асинхронны?
Я использую библиотеку запросов 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-запросов и отправки всех ответов на один обратный вызов. Это точно?
Ответы
Ответ 1
.map()
предназначен для параллельного запуска извлечения нескольких URL-адресов и действительно будет ожидать завершения этих задач (gevent.joinall(jobs)
)).
.send()
этого используйте .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()
он выполняет.
Ответ 2
Если вы не хотите использовать grequests
, вы можете просто реализовать запросы с обратными вызовами, используя модуль requests
+ the 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)
Ответ 3
Создайте список запросов, а затем отправьте их с помощью .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-адрес печатается