Асинхронные HTTP-вызовы в Python

Мне нужно использовать функцию обратного вызова в Python, где я отправляю запрос в webservice несколько раз с изменением параметра каждый раз. Я хочу, чтобы эти запросы выполнялись одновременно, а не последовательно, поэтому я хочу, чтобы функция вызывалась асинхронно.

Похоже, что asyncore - это то, что я могу использовать, но примеры, которые я видел о том, как все это работает, выглядят как overkill, поэтому мне интересно, есть ли еще один путь, по которому я должен идти. Любые предложения по модулям/процессу? В идеале я бы хотел использовать их в процедурной форме вместо создания классов, но я, возможно, не смогу обойти это.

Ответ 1

Twisted framework - это просто билет для этого. Но если вы не хотите этого делать, вы также можете использовать pycurl, wrapper для libcurl, который имеет свой собственный цикл событий async и поддерживает обратные вызовы.

Ответ 2

Знаете ли вы о eventlet? Он позволяет писать то, что представляется синхронным кодом, но работать он асинхронно по сети.

Вот пример супермаксимального искателя:

urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
     "https://wiki.secondlife.com/w/images/secondlife.jpg",
     "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]

import eventlet
from eventlet.green import urllib2

def fetch(url):

  return urllib2.urlopen(url).read()

pool = eventlet.GreenPool()

for body in pool.imap(fetch, urls):
  print "got body", len(body)

Ответ 3

Начиная с Python 3.2, вы можете использовать concurrent.futures для запуска параллельных задач.

Посмотрите пример ThreadPoolExecutor:

http://docs.python.org/dev/library/concurrent.futures.html#threadpoolexecutor-example

Он генерирует потоки для извлечения HTML и действует по ответам по мере их получения.

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the url and contents
def load_url(url, timeout):
    conn = urllib.request.urlopen(url, timeout=timeout)
    return conn.readall()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

В приведенном выше примере используется потоковая обработка. Существует также аналогичный ProcessPoolExecutor, который использует пул процессов, а не потоки:

http://docs.python.org/dev/library/concurrent.futures.html#processpoolexecutor-example

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the url and contents
def load_url(url, timeout):
    conn = urllib.request.urlopen(url, timeout=timeout)
    return conn.readall()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

Ответ 4

(Хотя этот поток касается серверного Python.Поскольку этот вопрос был задан некоторое время назад, другие могут наткнуться на него, где они ищут аналогичный ответ на стороне клиента)

Для клиентского решения вы можете захотеть взглянуть на библиотеку Async.js, особенно на раздел "Control-Flow".

https://github.com/caolan/async#control-flow

Объединив "Параллель" с "Водопадом", вы можете достичь желаемого результата.

WaterFall (параллельная (TaskA, TaskB, TaskC) → PostParallelTask)

Если вы посмотрите пример в Control-Flow - "Auto", они приведут пример: https://github.com/caolan/async#autotasks-callback где "файл записи" зависит от "get_data" и "make_folder" и "email_link" зависит от файла записи ".

Обратите внимание, что все это происходит на стороне клиента (если вы не делаете Node.JS - на стороне сервера)

Для серверного Python посмотрите на PyCURL @https://github.com/pycurl/pycurl/blob/master/examples/basicfirst.py

Объединив приведенный ниже пример с pyCurl, вы можете добиться неблокирующей многопоточной функции.

Надеюсь, это поможет. Удачи.

Venkatt @http://MyThinkpond.com