Механизм приложения DeadlineExceededError для заданий cron и очереди задач для сканера Википедии - PullRequest
2 голосов
/ 13 октября 2010

Я пытаюсь создать сканер ссылок в Википедии на Google App Engine.Я хотел сохранить индекс в хранилище данных.Но я сталкиваюсь с DeadlineExceededError как для заданий cron, так и для очереди заданий.

для задания cron У меня есть этот код:

def buildTree(self):</p> <pre><code> start=time.time() self.log.info(" Start Time: %f" % start) nobranches=TreeNode.all() for tree in nobranches: if tree.branches==[]: self.addBranches(tree) time.sleep(1) if (time.time()-start) > 10 : break self.log.info("Time Eclipsed: %f" % (time.time()-start)) self.log.info(" End Time:%f" % time.clock())

Я не понимаю, почему цикл for не прерывается через 10 секунд.Это происходит на сервере разработки.Что-то должно быть не так с time.time () на сервере.Есть ли другая функция, которую я могу использовать?

для очереди задач У меня есть этот код:
def addNewBranch(self, keyword, level=0):</p> <pre><code> self.log.debug("Add Tree") self.addBranches(keyword) t=TreeNode.gql("WHERE name=:1", keyword).get() branches=t.nodes if level < 3: for branch in branches: if branch.branches == []: taskqueue.add(url="/addTree/%s" % branch.name) self.log.debug("url:%s" % "/addTree/%s" % branch.name)

В журналах показано, что они обазапустить в DeadlineExceededError.Разве фоновая обработка не должна превышать 30 секунд для запроса страницы.Есть ли способ обойти исключение?

Вот код для addBranch () </p> <p>def addBranches(self, keyword): </p> <pre><code> tree=TreeNode.gql("WHERE name=:1", keyword).get() if tree is None: tree=TreeNode(name=keyword) self.log.debug("in addBranches arguments: tree %s", tree.name) t=urllib2.quote(tree.name.encode('utf8')) s="http://en.wikipedia.org/w/api.php?action=query&titles=%s&prop=links&pllimit=500&format=xml" % t self.log.debug(s) try: usock = urllib2.urlopen(s) except : self.log.error( "Could not retrieve doc: %s" % tree.name) usock=None if usock is not None: try: xmldoc=minidom.parse(usock) except Exception , error: self.log.error("Parse Error: %s" % error) return None usock.close() try: pyNode= xmldoc.getElementsByTagName('pl') self.log.debug("Nodes to be added: %d" % pyNode.length) except Exception, e: pyNode=None self.log.error("Getting Nodes Error: %s" % e) return None newNodes=[] if pyNode is not None: for child in pyNode: node=None node= TreeNode.gql("WHERE name=:1", child.attributes["title"].value).get() if node is None: newNodes.append(TreeNode(name=child.attributes["title"].value)) else: tree.branches.append(node.key()) db.put(newNodes) for node in newNodes: tree.branches.append(node.key()) self.log.debug("Node Added: %s" % node.name) tree.put() return tree.branches

Ответы [ 4 ]

2 голосов
/ 13 октября 2010

У меня был большой успех с datetime на GAE.

from datetime import datetime, timedelta
time_start = datetime.now()
time_taken = datetime.now() - time_start

time_taken будет timedelta. Вы можете сравнить его с другой временной шкалой, которая имеет интересующую вас продолжительность.

ten_seconds = timedelta(seconds=10)
if time_taken > ten_seconds:
    ....do something quick.

Звучит так, как будто бы вам лучше справиться с использованием mapreduce или Task Queues. И то, и другое отлично подходит для работы с огромным количеством записей.

Более чистым шаблоном для кода, который вы имеете, является выборка только некоторых записей.

nobranches=TreeNode.all().fetch(100)

Этот код будет извлекать только 100 записей. Если у вас есть целых 100, когда вы закончите, вы можете бросить еще один элемент в очередь, чтобы запустить больше.

- На основании комментария о необходимости деревьев без веток -

Я не вижу вашу модель там, но если бы я пытался создать список всех деревьев без ветвей и обработать их, я бы: Получить ключи только для деревьев в блоках по 100 или около того. Затем я выбрал бы все ветви, которые принадлежат этим деревьям, используя запрос In. Заказ по дереву ключом. Просканируйте список ветвей, при первом обнаружении ключа дерева вытащите дерево ключей из списка. Когда вы закончите, у вас будет список «безветвленных» ключей дерева. Запланируйте каждый из них для обработки.

Более простая версия - использовать MapReduce на деревьях. Для каждого дерева найдите одну ветвь, которая соответствует его идентификатору. Если вы не можете, отметьте дерево для продолжения. По умолчанию эта функция будет тянуть партии деревьев (я думаю, 25) с 8 одновременными работниками. Кроме того, он управляет внутренними очередями заданий, поэтому вам не нужно беспокоиться о времени ожидания.

1 голос
/ 13 октября 2010

Проблема в том, что вы выполняете операцию запроса для каждой ссылки в вашем документе. Так как на страницах Википедии может быть много ссылок, это означает много запросов - и, следовательно, у вас заканчивается время обработки. Этот подход также потребляет вашу квоту с фантастической скоростью!

Вместо этого вы должны использовать имя страницы на странице Википедии в качестве ключевого имени объекта. Затем вы можете собрать все ссылки из документа в список, создать ключи из них (что является полностью локальной операцией) и выполнить один пакетный db.get для всех них. После того, как вы обновили и / или создали их соответствующим образом, вы можете выполнить пакетный db.put, чтобы сохранить их все в хранилище данных - сократив общее количество операций хранилища данных с нумерованных ссылок * 2 до всего 2!

1 голос
/ 13 октября 2010

Когда происходят DeadlineExcededErrors, вы хотите, чтобы запрос в конечном итоге успешно выполнялся при повторном вызове. Для этого может потребоваться, чтобы в вашем состоянии сканирования был достигнут определенный прогресс, который можно пропустить в следующий раз. (Здесь не указывается)

Параллельные вызовы могут очень помочь.

  • Urlfetch
  • Datastore Put (смешанные объекты вместе, используя db.put)
  • Запрос хранилища данных (запросы параллельно - asynctools)

UrlFetch:

  • При выполнении вызовов urlfetch обязательно используйте асинхронный режим, чтобы свернуть цикл.

Datastore

  • Объединение сущностей, помещаемых в одну поездку в оба конца.

    # put newNodes+tree at the same time
    db.put(newNodes+tree)
    
  • Потяните TreeNode.gql из внутреннего цикла в инструмент параллельных запросов, например asynctools http://asynctools.googlecode.com

Asynctools Пример

    if pyNode is not None:

        runner = AsyncMultiTask()
        for child in pyNode:
             title = child.attributes["title"].value
             query = db.GqlQuery("SELECT __key__ FROM TreeNode WHERE name = :1", title)
             runner.append(QueryTask(query, limit=1, client_state=title))

        # kick off the work
        runner.run()

        # peel out the results
        treeNodes = []
        for task in runner:
            task_result = task.get_result() # will raise any exception that occurred for the given query
            treeNodes.append(task_result)

        for node in treeNodes:
            if node is None:
                newNodes.append(TreeNode(name=child.attributes["title"].value))

            else:
                tree.branches.append(node.key())
        for node in newNodes:
            tree.branches.append(node.key())
            self.log.debug("Node Added: %s" % node.name)

        # put newNodes+tree at the same time
        db.put(newNodes+tree)
        return tree.branches

РАСКРЫТИЕ ИНФОРМАЦИИ: Я связан с asynctools.

1 голос
/ 13 октября 2010

Не существует способа "обойти" исключение крайнего срока, кроме как заставить ваш код исполниться в надлежащие сроки.

...