6

如何在 App Engine 上运行后台任务?

4

8 回答 8

12

您可以使用任务队列 Python API

于 2009-06-23T02:11:39.343 回答
4

GAE 是构建可扩展 Web 应用程序的非常有用的工具。许多人指出的限制很少是不支持后台任务,缺乏周期性任务以及对每个HTTP请求花费多少时间的严格限制,如果请求超过该时间限制,则操作将被终止,这使得耗时的任务无法运行.

如何运行后台任务?
在 GAE 中,代码仅在有 HTTP 请求时执行。代码需要多长时间有严格的时间限制(我认为是 10 秒)。因此,如果没有请求,则不会执行代码。建议的解决方法之一是使用外部框连续发送请求,因此有点创建后台任务。但是为此我们需要一个外部盒子,现在我们依赖于一个额外的元素。另一种选择是发送 302 重定向响应,以便客户端重新发送请求,这也使我们依赖于外部元素,即客户端。如果那个外部盒子是 GAE 本身呢?每个使用过不支持循环结构的函数式语言的人都知道替代方案,即递归是循环的替代品。那么,如果我们完成部分计算并在同一个 url 上执行一个 HTTP GET 并以非常短的时间(比如 1 秒)呢?这会在 apache 上运行的 php 代码上创建一个循环(递归)。

<?php
$i = 0;
if(isset($_REQUEST["i"])){
        $i= $_REQUEST["i"];
    睡眠(1);
}
$ch = curl_init("http://localhost".$_SERVER["PHP_SELF"]."?i=".($i+1));
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
打印“你好世界\n”;
?>

一些这在 GAE 上不起作用。那么如果我们在其他一些 url 上做 HTTP GET 说 url2 哪个在第一个 url 上做 HTTP GET 呢?这似乎在 GAE 中有效。这个代码看起来像这样。

类 FirstUrl(webapp.RequestHandler):
    定义获取(自我):
        self.response.out.write("ok")
        时间.sleep(2)
        urlfetch.fetch("http://"+self.request.headers["HOST"]+'/url2')

类SecondUrl(webapp.RequestHandler):
    定义获取(自我):
        self.response.out.write("ok")
        时间.sleep(2)
        urlfetch.fetch("http://"+self.request.headers["HOST"]+'/url1')

application = webapp.WSGIApplication([('/url1', FirstUrl), ('/url2', SecondUrl)])
定义主():
    run_wsgi_app(应用程序)
如果 __name__ == "__main__":
    主要的()

由于我们找到了一种运行后台任务的方法,让我们为周期性任务(计时器)和一个跨越许多 HTTP 请求(foreach)的循环结构构建抽象。

计时器
现在构建计时器是直截了当的。基本思想是有一个计时器列表和每个应该被调用的时间间隔。一旦我们达到那个间隔,就调用回调函数。我们将使用 memcache 来维护计时器列表。为了找出何时调用回调,我们将在 memcache 中存储一个 key,其间隔为过期时间。我们定期(比如 5 秒)检查该键是否存在,如果不存在则调用回调并再次设置该键的间隔。

def 计时器(函数,间隔):
    timerlist = memcache.get('timer')
    如果(无 == 计时器列表):
        计时器列表 = []
    timerlist.append({'func':func, 'interval':interval})
    memcache.set('timer-'+func, '1', 间隔)
    memcache.set('timer', timerlist)

定义检查计时器():
    timerlist = memcache.get('timer')
    如果(无 == 计时器列表):
        返回假
    对于计时器列表中的电流:
        if(None == memcache.get('timer-'+current['func'])):
            #重置间隔
            memcache.set('timer-'+current['func'], '1', current['interval'])
            #调用回调函数
            尝试:
                eval(当前['func']+'()')
            除了:
                经过
            返回真
    返回假

Foreach
当我们想要进行长时间的计算时,例如对 1000 个数据库行进行一些操作或获取 1000 个 url 等,这是需要的。基本思想是在 memcache 中维护回调和参数列表,并且每次使用参数调用回调。

def foreach(函数,参数):
    looplist = memcache.get('foreach')
    如果(无 == 循环列表):
        循环列表 = []
    looplist.append({'func':func, 'args':args})
    memcache.set('foreach', looplist)

定义检查循环():
    looplist = memcache.get('foreach')
    如果(无 == 循环列表):
        返回假
    if((len(looplist) > 0) 和 (len(looplist[0]['args']) > 0)):
        arg = looplist[0]['args'].pop(0)
        func = looplist[0]['func']
        if(len(looplist[0]['args']) == 0):
            looplist.pop(0)
        if((len(looplist) > 0) 和 (len(looplist[0]['args']) > 0)):
            memcache.set('foreach', looplist)
        别的:
            memcache.delete('foreach')
        尝试:
            eval(func+'('+repr(arg)+')')
        除了:
            经过
        返回真
    别的:
        返回假

# 代替
# foreach 范围内的索引(0, 1000):
# someoperaton(index)
# 我们会说
# foreach('someoperaton', range(0, 1000))

现在构建一个每隔一小时获取 url 列表的程序是直截了当的。这是代码。

def getone(网址):
    尝试:
        结果 = urlfetch.fetch(url)
        如果(结果.status_code == 200):
            memcache.set(url, '1', 60*60)
            #处理结果.内容
    除了 :
        经过

def getallurl():
    #要获取的url列表
    urllist = ['http://www.google.com/', 'http://www.cnn.com/', 'http://www.yahoo.com', 'http://news.google. com']
    提取列表 = []
    对于 urllist 中的 url:
        如果(memcache.get(url) 为无):
            fetchlist.append(url)
    #这相当于
    #for fetchlist 中的 url: getone(url)
    if(len(fetchlist) > 0):
        foreach('getone', fetchlist)

#注册定时器回调
计时器('getallurl',3*60)

完整的代码在这里http://groups.google.com/group/httpmr-discuss/t/1648611a54c01aa 我已经在 appengine 上运行这段代码几天了,没有太多问题。

警告:我们大量使用 urlfetch。每天 urlfetch 的限制是 160000。所以要小心不要达到这个限制。

于 2008-09-29T15:51:15.347 回答
4

您可以在此处找到有关 Python App Engine 中 cron 作业的更多信息。

于 2012-11-01T18:03:30.383 回答
2

即将推出的运行时版本将具有某种周期性执行引擎 a'la cron。在 AppEngine 组上查看此消息。

所以,所有的 SDK 部分似乎都可以工作,但我的测试表明这还没有在生产服务器上运行——我设置了一个“每 1 分钟”的 cron 来记录它运行时的日志,并且它还没有被调用

很难说什么时候可以使用,虽然......

于 2009-02-06T15:20:34.663 回答
1

使用延迟 Python 库是使用基于 TaskQueue API 构建的 Python 在 Appengine 上执行后台任务的最简单方法。

from google.appengine.ext import deferred

def do_something_expensive(a, b, c=None):
    logging.info("Doing something expensive!")
    # Do your work here

# Somewhere else
deferred.defer(do_something_expensive, "Hello, world!", 42, c=True)
于 2016-01-28T14:14:57.637 回答
0

如果您想运行后台定期任务,请参阅此问题(AppEngine cron)

如果您的任务不是周期性的,请参阅任务队列 Python API任务队列 Java API

于 2012-03-06T17:28:21.707 回答
0

应用引擎中内置了一个 cron 工具。

请参考: https ://developers.google.com/appengine/docs/python/config/cron?hl=en

于 2013-08-14T18:03:08.987 回答
-3

使用任务队列 - http://code.google.com/appengine/docs/java/taskqueue/overview.html

于 2011-12-06T21:00:49.090 回答