Фоновые задачи в App Engine

Как я могу запускать фоновые задачи в App Engine?

Вероятно, вам следует удалить тег php, поскольку php не работает с GAE.

Christian Davén 30.09.2008 14:04
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
1
7 444
8

Ответы 8

GAE - очень полезный инструмент для создания масштабируемых веб-приложений. Некоторые из ограничений, на которые указывают многие, включают отсутствие поддержки фоновых задач, отсутствие периодических задач и строгие ограничения на то, сколько времени занимает каждый HTTP-запрос; если запрос превышает этот временной лимит, операция завершается, что делает невозможным выполнение трудоемких задач. .

Как запустить фоновую задачу?
В GAE код выполняется только при наличии HTTP-запроса. Существует строгий лимит времени (я думаю, 10 секунд) на то, сколько времени может занять код. Поэтому, если запросов нет, код не выполняется. Одним из предложенных способов решения проблемы было использование внешнего ящика для непрерывной отправки запросов, чтобы создать фоновую задачу. Но для этого нам нужен внешний бокс, и теперь мы зависим от еще одного элемента. Другой альтернативой была отправка ответа перенаправления 302, чтобы клиент повторно отправил запрос, это также делает нас зависимыми от внешнего элемента, который является клиентом. Что, если этот внешний блок - это сама GAE? Каждый, кто использовал функциональный язык, который не поддерживает конструкцию цикла, знает об альтернативе, т.е. рекурсия является заменой цикла. Так что, если мы завершим часть вычислений и выполним HTTP GET на том же URL-адресе с очень коротким тайм-аутом, скажем, 1 секунда? Это создает цикл (рекурсию) в PHP-коде, запущенном на apache.

<?php
$i = 0;
if (isset($_REQUEST["i"])){
        $i= $_REQUEST["i"];
    sleep(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);
print "hello world\n";
?>

Кое-как это не работает на GAE. Так что, если мы выполним HTTP GET на каком-то другом URL-адресе, например url2, который выполняет HTTP GET на первом URL-адресе? Кажется, это работает в GAE. Код для этого выглядит так.

class FirstUrl(webapp.RequestHandler):
    def get(self):
        self.response.out.write("ok")
        time.sleep(2)
        urlfetch.fetch("http://"+self.request.headers["HOST"]+'/url2')

class SecondUrl(webapp.RequestHandler):
    def get(self):
        self.response.out.write("ok")
        time.sleep(2)
        urlfetch.fetch("http://"+self.request.headers["HOST"]+'/url1')

application = webapp.WSGIApplication([('/url1', FirstUrl), ('/url2', SecondUrl)])
def main():
    run_wsgi_app(application)
if __name__ == "__main__":
    main()

Поскольку мы обнаружили способ запуска фоновой задачи, давайте создадим абстракции для периодической задачи (таймер) и конструкцию цикла, которая охватывает множество HTTP-запросов (foreach).

Таймер
Теперь строить таймер проще простого. Основная идея состоит в том, чтобы иметь список таймеров и интервал, с которым каждый из них должен вызываться. Как только мы достигнем этого интервала, вызовите функцию обратного вызова. Мы будем использовать кэш памяти для ведения списка таймеров. Чтобы узнать, когда вызывать обратный вызов, мы сохраним ключ в кэше памяти с интервалом в качестве времени истечения срока действия. Мы периодически (скажем, 5 секунд) проверяем, присутствует ли этот ключ, если нет, то вызываем обратный вызов и снова устанавливаем этот ключ с интервалом.

def timer(func, interval):
    timerlist = memcache.get('timer')
    if (None == timerlist):
        timerlist = []
    timerlist.append({'func':func, 'interval':interval})
    memcache.set('timer-'+func, '1', interval)
    memcache.set('timer', timerlist)

def checktimers():
    timerlist = memcache.get('timer')
    if (None == timerlist):
        return False
    for current in timerlist:
        if (None == memcache.get('timer-'+current['func'])):
            #reset interval
            memcache.set('timer-'+current['func'], '1', current['interval'])
            #invoke callback function
            try:
                eval(current['func']+'()')
            except:
                pass
            return True
    return False

Для каждого
Это необходимо, когда мы хотим выполнить длительные вычисления, скажем, выполнить некоторую операцию с 1000 строками базы данных или получить 1000 URL-адресов и т. д. Основная идея состоит в том, чтобы поддерживать список обратных вызовов и аргументов в кэше памяти и каждый раз вызывать обратный вызов с аргументом.

def foreach(func, args):
    looplist = memcache.get('foreach')
    if (None == looplist):
        looplist = []
    looplist.append({'func':func, 'args':args})
    memcache.set('foreach', looplist)

def checkloops():
    looplist = memcache.get('foreach')
    if (None == looplist):
        return False
    if ((len(looplist) > 0) and (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) and (len(looplist[0]['args']) > 0)):
            memcache.set('foreach', looplist)
        else:
            memcache.delete('foreach')
        try:
            eval(func+'('+repr(arg)+')')
        except:
            pass
        return True
    else:
        return False

# instead of
# foreach index in range(0, 1000):
#   someoperaton(index)
# we will say
# foreach('someoperaton', range(0, 1000))

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

def getone(url):
    try:
        result = urlfetch.fetch(url)
        if (result.status_code == 200):
            memcache.set(url, '1', 60*60)
            #process result.content
    except :
        pass

def getallurl():
    #list of urls to be fetched
    urllist = ['http://www.google.com/', 'http://www.cnn.com/', 'http://www.yahoo.com', 'http://news.google.com']
    fetchlist = []
    for url in urllist:
        if (memcache.get(url) is None):
            fetchlist.append(url)
    #this is equivalent to
    #for url in fetchlist: getone(url)
    if (len(fetchlist) > 0):
        foreach('getone', fetchlist)

#register the timer callback
timer('getallurl', 3*60)

полный код здесь http://groups.google.com/group/httpmr-discuss/t/1648611a54c01aa Я запускал этот код на appengine в течение нескольких дней без особых проблем.

Предупреждение: мы активно используем urlfetch. Предел количества urlfetch в день составляет 160000. Так что будьте осторожны, чтобы не достигнуть этого лимита.

Я не понимаю, как это может сработать. Вы не превысите 10-секундную квоту на 6-ю рекурсивную выборку?

Constantin 04.10.2008 00:43

Поправьте меня, если я ошибаюсь, разве нет политики AppEngine в отношении взаимодействия между размещенными приложениями?

zotherstupidguy 05.05.2009 01:12

Используйте новый API задач или API Cron. не используйте вышеперечисленные.

Kinlan 23.06.2009 19:36

В грядущей версии среды выполнения будет какой-то механизм периодического выполнения a'la cron. См. это сообщение в группе AppEngine.

So, all the SDK pieces appear to work, but my testing indicates this isn't running on the production servers yet-- I set up an "every 1 minutes" cron that logs when it runs, and it hasn't been called yet

Но когда это будет доступно, сказать сложно ...

Вы можете использовать API очереди задач Python.

Используйте очередь задач - http://code.google.com/appengine/docs/java/taskqueue/overview.html

-1; это, по сути, тот же ответ, опубликованный и принятый 2 года назад, со ссылкой на документы Java вместо документов Python, когда OP использовал Python. Серьезно?

Wooble 07.12.2011 17:33

Если вы хотите запускать периодические фоновые задачи, см. этот вопрос (AppEngine cron)

Если ваши задачи не периодические, см. API очереди задач Python или API очереди задач Java

Дополнительные сведения о заданиях cron можно найти в Python App Engine здесь.

В движок приложения встроено средство cron.

Пожалуйста, обратитесь к: https://developers.google.com/appengine/docs/python/config/cron?hl=en

Использование Отложенная библиотека Python - это самый простой способ выполнить фоновую задачу в Appengine с использованием Python, который построен поверх TaskQueue API.

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)

У использования отложенной библиотеки есть свои плюсы и минусы, см. Раздел «Когда использовать ext.deferred» в статье Ника Джонсона: cloud.google.com/appengine/articles/deferred?hl=en

Dan Cornilescu 28.01.2016 17:28

Другие вопросы по теме