Асинхронная выборка URL, когда нас не волнует результат? [Питон]

В каком-то коде, который я пишу для GAE, мне нужно периодически выполнять GET для URL-адреса в другой системе, по сути, «пинговать» его, и меня не очень беспокоит, если запрос завершается неудачно, истекает время ожидания или завершается успешно.

Поскольку я в основном хочу «запустить и забыть», а не замедлять свой собственный код, ожидая запроса, я использую асинхронный urfetch и не вызываю get_result().

В моем журнале я получаю предупреждение:

Обнаружен 1 запрос RPC без соответствующего ответа (предположительно, из-за тайм-аутов или других ошибок)

Я пропустил явно лучший способ сделать это? Очередь задач или отложенная задача кажутся (мне) излишними в этом случае.

Любой вклад будет оценен.


person John Carter    schedule 23.03.2011    source источник
comment
Я предполагаю, что это Python, а не Java?   -  person jiggy    schedule 23.03.2011
comment
Вы правы. Я обновил вопрос. Хороший улов.   -  person John Carter    schedule 24.03.2011
comment
Вы должны определить, завершается ли операция URLFetch, и если да, то происходит ли это до или после того, как ваш запрос возвращается вызывающей стороне. Я думаю, вы обнаружите, что он заканчивается и что wait происходит неявно после исходного запроса, возвращающего свой результат. Однако я нигде не нашел подробной документации для этого, поэтому поведение может быть изменено.   -  person technomage    schedule 18.01.2014


Ответы (2)


Задача очереди задач — ваш лучший вариант здесь. Сообщение, которое вы видите в журнале, указывает на то, что запрос ожидает завершения вашего URLFetch перед возвратом, поэтому это не помогает. Вы говорите, что задача «излишняя», но на самом деле они очень легкие и, безусловно, лучший способ сделать это. Deferred даже позволит вам просто отложить вызов выборки напрямую, вместо того, чтобы писать функцию для вызова.

person Nick Johnson    schedule 23.03.2011

Сколько времени требуется для завершения async_url_fetch и сколько времени требуется для предоставления вашего ответа?

Вот возможный подход к использованию того, как API работает в python.

Некоторые моменты для рассмотрения.

  • Многие веб-серверы и обратные прокси-серверы не отменяют запрос после его запуска. Поэтому, если ваш удаленный сервер, на который вы пингуете, передает запрос, но его обслуживание занимает много времени, используйте крайний срок для вашего create_rpc(deadline=X), чтобы X возвращался из-за тайм-аута. Пинг все еще может быть успешным. Этот метод работает и против самого аппенгина.

  • МПК GAE

    • RPCs after being cued via make_call/make_fetch_call are actually only dispatched once one of them is waited on.
    • Кроме того, любой только что завершившийся rpc будет вызывать свой обратный вызов, когда завершится текущий ожидающий.
    • Вы можете создать async_urlfetch rpc и поставить его в очередь с помощью make_fetch_call как можно раньше при обработке вашего запроса, пока не ждите.
    • Выполняйте фактическую работу по обслуживанию страниц, например вызовы memcache/datastore, чтобы начать работу. Первый вызов одного из них вызовет ожидание, которое отправит ваш async_urlfetch.
    • Если urfetch завершится во время этого другого действия, будет вызван обратный вызов urfetch, что позволит вам обработать результат.
    • Если вы вызовете get_result(), он будет заблокирован на wait() до крайнего срока или вернется, если результат не будет готов.

Резюме.

Подготовьте долгоиграющий url_fetch с разумным сроком и обратным вызовом. Поставьте его в очередь с помощью make_fetch_call. Выполните работу, которую вы хотели для страницы. Вернуть страницу независимо от того, завершен ли url_fetch или истек срок, и не дожидаясь его.

Базовый уровень RPC в GAE полностью асинхронный, кажется, есть более сложный способ выбрать то, что вы хотите подождать в работе.

В этих примерах используется сон и url_fetch для второго экземпляра того же приложения.

Пример работы rpc с помощью wait():

class AsyncHandler(RequestHandler):

    def get(self, sleepy=0.0):
        _log.info("create rpc")
        rpc = create_rpc()
        _log.info("make fetch call")
        # url will generate a 404
        make_fetch_call(rpc, url="http://<my_app>.appspot.com/hereiam")
        _log.info("sleep for %r", sleepy)
        sleep(sleepy)
        _log.info("wait")
        rpc.wait()
        _log.info("get_result")
        rpc.get_result()
        _log.info("return")
        return "<BODY><H1>Holla %r</H1></BODY>" % sleepy

Ожидание, вызванное после сна в течение 4 секунд, показывает отправку

2011-03-23 17:08:35.673 /delay/4.0 200 4093ms 23cpu_ms 0kb Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10
E 2011-03-23 17:27:45.446 404: Not Found Traceback (most recent call last): File "distlib/tipfy/__init__.py", line 430, in wsgi_app rv = self.dispatch(request) File "di
I 2011-03-23 17:27:45.449 Saved; key: __appstats__:065400, part: 27 bytes, full: 835 bytes, overhead: 0.000 + 0.002; link: http://<myapp>.appspot.com/_ah/stats/details?time
7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16,gzip(gfe) I 2011-03-23 17:08:31.583 create rpc I 2011-03-23 17:08:31.583 make fetch call I 2011-03-23 17:08:31.585 sleep for 4.0 I 2011-03-23 17:08:35.585 wait I 2011-03-23 17:08:35.663 get_result I 2011-03-23 17:08:35.663 return I 2011-03-23 17:08:35.669 Saved; key: __appstats__:011500, part: 48 bytes, full: 4351 bytes, overhead: 0.000 + 0.006; link: http://<myapp>.appspot.com/_ah/stats/details?tim 2011-03-23 17:08:35.636 /hereiam 404 9ms 0cpu_ms 0kb AppEngine-Google; (+http://code.google.com/appengine; appid: s~<myapp>),gzip(gfe)

Асинхронный отправленный вызов.

E 2011-03-23 17:08:35.632 404: Not Found Traceback (most recent call last): File "distlib/tipfy/__init__.py", line 430, in wsgi_app rv = self.dispatch(request) File "di
I 2011-03-23 17:08:35.634 Saved; key: __appstats__:015600, part: 27 bytes, full: 836 bytes, overhead: 0.000 + 0.002; link: http://<myapp>.appspot.com/_ah/stats/details?time

Демонстрация использования memcache RPC, ожидающего начала работы.

class AsyncHandler(RequestHandler):

    def get(self, sleepy=0.0):
        _log.info("create rpc")
        rpc = create_rpc()
        _log.info("make fetch call")
        make_fetch_call(rpc, url="http://<myapp>.appspot.com/hereiam")
        _log.info("sleep for %r", sleepy)
        sleep(sleepy)
        _log.info("memcache's wait")
        memcache.get('foo')
        _log.info("sleep again")
        sleep(sleepy)
        _log.info("return")
        return "<BODY><H1>Holla %r</H1></BODY>" % sleepy

Журнал продукции Appengine:

2011-03-23 17:27:47.389 /delay/2.0 200 4018ms 23cpu_ms 0kb Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10
E 2011-03-23 17:27:45.446 404: Not Found Traceback (most recent call last): File "distlib/tipfy/__init__.py", line 430, in wsgi_app rv = self.dispatch(request) File "di
I 2011-03-23 17:27:45.449 Saved; key: __appstats__:065400, part: 27 bytes, full: 835 bytes, overhead: 0.000 + 0.002; link: http://<myapp>.appspot.com/_ah/stats/details?time
7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16,gzip(gfe) I 2011-03-23 17:27:43.374 create rpc I 2011-03-23 17:27:43.375 make fetch call I 2011-03-23 17:27:43.377 sleep for 2.0 I 2011-03-23 17:27:45.378 memcache's wait I 2011-03-23 17:27:45.382 sleep again I 2011-03-23 17:27:47.382 return W 2011-03-23 17:27:47.383 Found 1 RPC request(s) without matching response (presumably due to timeouts or other errors) I 2011-03-23 17:27:47.386 Saved; key: __appstats__:063300, part: 66 bytes, full: 6869 bytes, overhead: 0.000 + 0.003; link: http://<myapp>.appspot.com/_ah/stats/details?tim 2011-03-23 17:27:45.452 /hereiam 404 10ms 0cpu_ms 0kb AppEngine-Google; (+http://code.google.com/appengine; appid: s~<myapp>),gzip(gfe)

Асинхронная выборка URL-адреса отправляется, когда memcache.get вызывает wait()

E 2011-03-23 17:27:45.446 404: Not Found Traceback (most recent call last): File "distlib/tipfy/__init__.py", line 430, in wsgi_app rv = self.dispatch(request) File "di
I 2011-03-23 17:27:45.449 Saved; key: __appstats__:065400, part: 27 bytes, full: 835 bytes, overhead: 0.000 + 0.002; link: http://<myapp>.appspot.com/_ah/stats/details?time
person kevpie    schedule 23.03.2011
comment
Это не простое решение, но оно предназначено для того, чтобы дать пищу для размышлений. - person kevpie; 24.03.2011
comment
Хорошая мысль о тайм-аутах. Я почти уверен, что вы ошибаетесь в том, что RPC отправляются только после вызова ожидания - это применимо только к dev_appserver, насколько мне известно. - person Nick Johnson; 24.03.2011
comment
@Ник, мне пришлось перепроверить. Документация в источнике в настоящее время может предсказывать будущее или слегка вводить в заблуждение. Это то, с чем почти никто не столкнется, если только они серьезно не запутались в выполнении асинхронных вызовов API. После создания asynctools у меня была мечта создать асинхронный преобразователь фрагментов страниц, который делал бы все параллельно. Год назад я работал над инструментом, который делал десятки подписанных вызовов API между facebook/twitter/linkedin с несколькими резервными ключами oauth/public в режиме реального времени. Параллелизм, который обеспечивает уровень GAE RPC, просто поразителен. - person kevpie; 24.03.2011
comment
@Джон, сейчас это действительно выходит из-под контроля. Воспользуйтесь предложением Ника, если оно случается, отложите его. Если периодично, то Cron это. - person kevpie; 24.03.2011
comment
Спасибо, Кев, хороший материал здесь. Очень ценю усилия, которые вы приложили к своему ответу. - person John Carter; 24.03.2011
comment
Утверждение о том, что RPC отправляются только после вызова get_result() или wait, неверно. - person technomage; 18.01.2014
comment
@technomage В то время (2009-2011) это были однопоточные, те, где точки, в которых управление передавалось на уровень RPC. Я не могу говорить о том, как это работает сейчас. - person kevpie; 19.01.2014