当我们不关心结果时如何异步获取URL?[Python]
在我为谷歌应用引擎(GAE)写的一些代码中,我需要定期向另一个系统的一个网址发送GET请求,简单来说就是“ping”一下这个网址。我并不是特别在意这个请求是失败、超时还是成功。
因为我基本上想要“发出请求就不管了”,不想因为等待请求的结果而拖慢我自己的代码,所以我使用了异步的urlfetch,并且没有调用get_result()。
在我的日志中,我收到了一个警告:
发现了1个RPC请求没有匹配的响应(可能是由于超时或其他错误)
我是不是漏掉了什么明显更好的做法?在我看来,使用任务队列或延迟任务在这种情况下似乎有点过于复杂了。
任何建议都非常感谢。
2 个回答
async_url_fetch 完成需要多长时间?提供响应又需要多长时间?
这里有一种可能的方法,可以利用 Python 中 API 的工作方式。
需要考虑的一些要点:
很多网络服务器和反向代理在请求开始后不会取消请求。所以如果你要访问的远程服务器接收了请求但处理起来很慢,可以在创建 RPC 时设置一个截止时间(deadline),比如 create_rpc(deadline=X),这样如果超时就会返回。虽然请求可能还是会成功。这种方法在 appengine 上也有效。
GAE RPCs
- 通过 make_call/make_fetch_call 发起的 RPC,实际上只有在等待其中一个时才会被真正处理。
- 而且任何刚完成的 RPC 会在当前等待的 RPC 完成时调用它的回调函数。
- 你可以尽早创建一个 async_urlfetch RPC,并使用 make_fetch_call 将其加入队列,但暂时不要等待它。
- 先做你想要的页面服务工作,比如调用 memcache/datastore 来开始处理。第一次调用其中一个时会进行等待,这样就会触发你的 async_urlfetch。
- 如果在其他活动进行时 urlfetch 完成了,它的回调函数会被调用,这样你就可以处理结果。
- 如果你调用 get_result(),它会在等待(wait())时阻塞,直到截止时间到或者结果准备好。
总结一下:
准备一个有合理截止时间和回调的长时间运行的 url_fetch。使用 make_fetch_call 将其加入队列。做你想要的页面工作。无论 url_fetch 是否完成或超时,都返回页面,而不需要等待它。
GAE 的底层 RPC 层都是异步的,似乎有一种更复杂的方法正在开发中,可以选择你想要等待的内容。
这些例子使用了 sleep 和对同一应用的第二个实例进行 url_fetch。
wait() 调度 RPC 工作的示例:
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 秒后调用 wait 显示调度的情况
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_6_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 的 wait 来启动工作。
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_6_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)
当 memcache.get 调用 wait() 时,异步 url fetch 被调度。
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
在这里,使用任务队列是最好的选择。你在日志中看到的消息表明,请求正在等待你的URLFetch完成后才能返回,这样并没有解决问题。你说任务是“过于复杂”,但其实它们非常轻量,绝对是处理这个问题的最佳方法。使用Deferred,你甚至可以直接延迟调用fetch,而不需要写一个函数来调用它。